In this project, I will attempt to predict whether, for any given parcel in Detroit with a building on it, whether a building on that parcel will be be targetted for demoltion. Potential predictors include citations relatated to the building, crime, and complaints concerning the building related to blight. The probject is currently at the data cleaning stage, and I may add some other data to the project. So far I am using about 3GB of data downloaded from https://data.detroitmi.gov/.

library(tidyverse)
library(sf)
library(ggmap)
library(lwgeom)
#recorded violations associated with blight (e.g. unkempt properties)
blight_violations <- read_csv("./data/Blight_Violations_3_19_2018.csv", 
                              guess_max = 10^6)
#read the downloaded file for all the building permits and then filter out the permits for dismantling
dismantle_permits <- read_csv("./data/Building_Permits_3_19_2018.csv", 
                             guess_max = 10^6) %>%
  filter(`Building Permit Type` == "Dismantle")
  
#the files that contain the crime data
crime_to_12062016 <- 
  read_csv("./data/DPD__All_Crime_Incidents__January_1__2009_-_December_6__2016.csv",
           guess_max = 10^6)
crime_12062016_to_03192018 <- 
  read_csv("./data/DPD__All_Crime_Incidents__December_6__2016_-_3_19_2018.csv", 
           guess_max = 10^6)
#the 311 system
improve_detroit_issues <- read_csv("./data/Improve_Detroit_Issues_3_19_2018.csv", 
                                   guess_max = 10^6)
#another file with demolition information downloaded 4/4/2017
completed_demolitions <- read_csv("./data/Detroit_Demolitions.csv",
                                  guess_max = 10^6)
#the shapefile representing Detroit parcels, read into 
parcel_sf <- st_read("./data/Parcel Map")
#convert the parcel number column to a character vector, to match `Parcel Number` in the dismantle permits data
parcel_sf <- parcel_sf %>% mutate(parcelnum = as.character(parcelnum))

For all of the downloaded datasets other than the parcels dataset, we extract the usable latutude and longitude values and then use this information to form simple features (sf) objects. Rows with obviously incorrect values, or values that would represent positions well outside Detroit, are filtered out, together with rows for which the latitude or longitude data is missing.

#function for converting the position (character) column into a column of points in the simple features (sf) framework.
add_sf_point <- function(df, column) {
  
  #extract the latitude and longitude from the string column that contains both. With the parentheses
  #located from the end of the strings, it is possible to use the the same function for all five of 
  #the datasets for which we need to extract this information.
  latitude <- str_sub(df[[column]], 
                      stringi::stri_locate_last_fixed(df[[column]], "(")[,2] + 1,
                      stringi::stri_locate_last_fixed(df[[column]], ",")[,1] - 1)
  longitude <- str_sub(df[[column]], 
                       stringi::stri_locate_last_fixed(df[[column]], ", ")[,2] + 1, 
                       stringi::stri_locate_last_fixed(df[[column]], ")")[,1] - 1)
  
  #add the latititude and and longitude to a copy of the dataframe, filter out the NAs from
  #these results, and then convert convert to sf, with point positions indicated in the
  #geometry column
  mutated <- df %>% mutate(extracted_lat = as.double(latitude),
                      extracted_lon = as.double(longitude))
  
  #remove rows with NAs for latitude or longitude, or with values well outside of Detroit
  filtered <- mutated %>%
    filter(!is.na(extracted_lat) & !is.na(extracted_lon)) %>%
    filter(41 < extracted_lat & extracted_lat < 44 & -85 < extracted_lon & extracted_lon < -81)
    
  #create a dataframe from the items that have been filtered out
  result_coord_na <- setdiff(mutated, filtered)
  
  #create sf objects from the rows with usable latitude and longitude information
  result_sf <- st_as_sf(filtered, coords = c("extracted_lon", "extracted_lat"), crs = 4326)
    
  return(list(result_sf, result_coord_na))
}
#apply the function to the five datasets for which the data was not loaded as a simple features dataframe, thus producing a list of two dataframes for each of the datasets, the first element of the list a simple features data frame and the second element a dataframe with the instances for which it was not possible to convert to simple features
blight_violations_split <- add_sf_point(blight_violations, "Violation Location")
dismantle_permits_split <- add_sf_point(dismantle_permits, "Permit Location")
crime_to_12062016_split <- add_sf_point(crime_to_12062016, "LOCATION")
crime_12062016_to_03192018_split <- add_sf_point(crime_12062016_to_03192018, "Location")
improve_detroit_issues_split <- add_sf_point(improve_detroit_issues, "Location")
completed_demolitions_split <- add_sf_point(completed_demolitions, "Location")

We now consider the data for which we do not yet have position data, and complete the information as well as we reasonably can, using the Google api and a function, geocode_pause, that handles some of api’s quirks.

#the portino of the downloaded blight citations data, for which we do not have
blight_vio_na <- blight_violations_split[[2]]
#remove the rows for which geocoding is not likely to prodoce reliable results
useful <- blight_vio_na %>% filter(!is.na(`Violation Street Name`), 
                                   `Violation Street Number` > 0, 
                                   !is.na(`Violation Zip Code`))
#create a column of addresses to be used in geocoding
useful <- useful %>% 
  mutate(complete_address = paste(`Violation Street Number`, " ", `Violation Street Name`, ", ",
                                  "Detroit, Michigan", " ", `Violation Zip Code`, sep = ""))
#function makes a maximum 6 attempts to geocode the given address using the Google API, with a pause of 1 second between attempts. We will use the function for the other datasets as well.
geocode_pause <- function(address) {
  for (index in 1:6) {
    Sys.sleep(1)
    location <- ggmap::geocode(address)
    if (!is.na(location$lon)) {
      return(location)
    }
  }
}
#apply geocode_pause to each of the elements of the complete_addresse column and place the result in a new column, in which each entry is a data frame 
useful <- useful %>% mutate(location = map(complete_address, geocode_pause))

#save to disc, to avoid avoid the need to geocode these addresses again when we rerun the analysis
write_rds(useful, "./data/blight_violations_geocodes.rds")

The geocoding has returned a data frame for each of the addresses. We thus need to unpack the elements of the location column, each of which is a data frame.

#read blight_violations_geocodes as a tibble
blight_violations_geocodes <- read_rds("./data/blight_violations_geocodes.rds")
#function for removing the instances for which geocoding failed (for which the value in the location column is NULLL). We will use this function for all of the geocoded data frames.
remove_null_locations <- function(df) {
  #identify the rows for which the value in the location column is NULL
  null_rows <- list()
  for (index in 1:nrow(df)) {
    if (is.null(df$location[[index]])) {
      null_rows <- c(null_rows, index)
    }
  }
  #remove the rows for which the value of the location column is NULL
  df <- df[-as.integer(null_rows),]
}
blight_violations_geocodes <- remove_null_locations(blight_violations_geocodes)
#With blight_violations_geocodes a tibble, we can apply tidyr::unnest(), which will place the latitude and longitude in columns labelled "lat" and "lon".
blight_violations_geocodes <- blight_violations_geocodes %>% unnest(location)
#fill in the `Violation Latitute` and `Violation Longitude` data frames, which alread exist in the blight_violations data frame
blight_violations_geocodes <- blight_violations_geocodes %>%
  mutate(`Violation Latitude` = lat,
         `Violation Longitude` = lon)
#cut out some columns that have been added
blight_violations_geocodes <- blight_violations_geocodes %>% 
  select(-extracted_lat, -extracted_lon, -complete_address)
#put the position information into a simple features format (which will remove the "lat" and "lon" columns)
blight_violations_geocodes_sf <- st_as_sf(blight_violations_geocodes, 
                                          coords = c("lon", "lat"),
                                          crs = 4326)
#combine the results with the previously generated sf data
blight_violations_sf <- rbind(blight_violations_split[[1]], blight_violations_geocodes_sf)
rm(blight_vio_na, blight_violations, blight_violations_geocodes, 
   blight_violations_geocodes_sf, blight_violations_split, useful)
#the dismantle permits for which position data (latitude and longitude) is missing
dismantle_permits_split_na <- dismantle_permits_split[[2]]
#remove the last two columns, which were not contained in the original dismantle_permits datastet
dismantle_permits_split_na <- dismantle_permits_split_na %>% 
  select(-extracted_lat, -extracted_lon)
#geocode the items in dismantle_permits_split_na, using the address column and the function geocode_pause, which makes a maximum of six attempts for each item. The result is list of dataframes in the location column.
dismantle_permits_split_geocode <- dismantle_permits_split_na %>%
  mutate(location = map(str_c(`Site Address`, ", Detroit, Michigan"), geocode_pause))

#write the results of the geocoding to disk, to avoid having to repeat the geocoding when rerunning the analysis.
write_rds(dismantle_permits_split_geocode, "./data/dismantle_permits_geocodes.rds")

rm(dismantle_permits_split_na)
#load the geocoded data frame into R
dismantle_permits_split_geocode <- read_rds("./data/dismantle_permits_geocodes.rds")
#use the remove_null_locations() to remove the rows for which geocoding failed and then parse the information  in the dataframes in the location column into two new columns, lat and lan
dismantle_permits_split_geocode <- 
  remove_null_locations(dismantle_permits_split_geocode) %>%
  unnest(location)
#convert to a simple features (sf) data frame, using the latititudes and longitudes
dismantle_permits_geocode_sf <- st_as_sf(dismantle_permits_split_geocode, 
                                          coords = c("lon", "lat"),
                                          crs = 4326)
#append this simple features dataframe to the dataframe for which we already had usable positions
dismantle_permits_sf <- rbind(dismantle_permits_split[[1]], dismantle_permits_geocode_sf)
rm(dismantle_permits_split_geocode, dismantle_permits_geocode_sf, dismantle_permits, dismantle_permits_split, dismantle_permits_split_na)

We now fill-in the missing position information for the dataset for crimes up to 12-06-2016

#return to the older crime data
crime_to_12062016_leftovers <- crime_to_12062016_split[[2]]
#cut out the addresses that begin with "00"
crime_to_12062016_leftovers <- crime_to_12062016_leftovers %>% 
  filter(str_sub(LOCATION, 1, 2) != "00")
#filter out some obviously useless addresses, with few characters before the first "("
crime_to_12062016_leftovers <- crime_to_12062016_leftovers %>% 
  filter(!str_locate(LOCATION, "\\(")[,1] %in% 1:13)
#remove the two columns that were added earlier
crime_to_12062016_leftovers <- crime_to_12062016_leftovers %>% 
  select(-extracted_lat, -extracted_lon)
#create a column for use in geocoding
crime_to_12062016_leftovers <- crime_to_12062016_leftovers %>% 
  mutate(extracted_address = str_c(str_sub(LOCATION, 1, 
                                           str_locate(LOCATION, "\\(")[,1] - 2),
                                                      ", Detroit, Michigan"))
#geocode the elements of extracted_address, using the function geocode_pause
crime_to_12062016_leftovers_geocode <- crime_to_12062016_leftovers %>% 
  mutate(location = map(extracted_address, geocode_pause))

#save the results, to avoid having to geocode again when rerunning the analysis
write_rds(crime_to_12062016_leftovers_geocode, "./data/crime_to_12062016_leftovers_geocode.rds")
crime_to_12062016_leftovers_geocode <- read_rds("./data/crime_to_12062016_leftovers_geocode.rds")
#cut out the column we used for geocoding
crime_to_12062016_leftovers_geocode <- 
  crime_to_12062016_leftovers_geocode %>% select(-extracted_address)
  
#cut out of the geocode failures and put the location information into the columns lat and lon
crime_to_12062016_leftovers_geocode <- 
  remove_null_locations(crime_to_12062016_leftovers_geocode) %>%
  unnest(location)
#create a simple features (sf) object, using the latititudes and longitudes
crime_to_12062016_leftovers_sf <- st_as_sf(crime_to_12062016_leftovers_geocode, 
                                          coords = c("lon", "lat"),
                                          crs = 4326)
#append this simple features dataframe to the dataframe for which we already had locations
crime_to_12062016_sf <- rbind(crime_to_12062016_split[[1]], crime_to_12062016_leftovers_sf)
rm(crime_to_12062016, crime_to_12062016_leftovers_geocode, crime_to_12062016_leftovers_sf, crime_to_12062016_leftovers, crime_to_12062016_split)
#consider the examples in the recent crime data for which the conversion to sf didn't work, remove the two columns that we have added, and create and address column for geocoding
crime_12062016_to_03192018_leftovers <- crime_12062016_to_03192018_split[[2]] %>%
  select(-extracted_lat, -extracted_lon) %>%
  mutate(extracted_address = str_c(`Incident Address`, ", Detroit, Michigan"))
crime_12062016_to_03192018_geocode <- crime_12062016_to_03192018_leftovers %>% 
  mutate(location = map(extracted_address, geocode_pause))

write_rds(crime_12062016_to_03192018_geocode, "./data/crime_12062016_to_03192018_geocode.rds")
crime_12062016_to_03192018_geocode <- read_rds("./data/crime_12062016_to_03192018_geocode.rds") %>%
  select(-extracted_address)
#remove the rows for which the value of location is NULL and then unnest the remaining locations
crime_12062016_to_03192018_geocode <- 
  remove_null_locations(crime_12062016_to_03192018_geocode) %>%
  unnest(location)
#convert the dataframe to a simple features set
crime_12062016_to_03192018_sf <- st_as_sf(crime_12062016_to_03192018_geocode, 
                                          coords = c("lon", "lat"),
                                          crs = 4326)  
#combine the geocoded data with the sf dataframe created earlier
crime_12062016_to_03192018 <- rbind(crime_12062016_to_03192018_split[[1]], crime_12062016_to_03192018_sf)
rm(crime_12062016_to_03192018_split, crime_12062016_to_03192018_geocode, crime_12062016_to_03192018_leftovers, crime_12062016_to_03192018_sf)
#geocode the one item in the Improve Detroit Issues data for which the given coordinates were obviously incorrect, and then convert to an sf object. If geocoding fails, run this bit again
improve_detroit_issues_leftover_sf <- improve_detroit_issues_split[[2]] %>%
  select(-extracted_lat, -extracted_lon) %>%
  mutate(location = map(Address, geocode_pause)) %>%
  unnest(location) %>% 
  st_as_sf(coords = c("lon", "lat"), crs = 4326)  
Information from URL : http://maps.googleapis.com/maps/api/geocode/json?address=14530%20%20Vaughan%20Detroit,%20Michigan&sensor=false
#splice with the previously generated sf dataframe
improve_detroit_issues <- rbind(improve_detroit_issues_split[[1]], improve_detroit_issues_leftover_sf)
rm(improve_detroit_issues_split, improve_detroit_issues_leftover_sf)
#create the other set of demolition information
completed_demolitions_sf <- completed_demolitions_split[[1]]
#note that location information in this dataset is complete
completed_demolitions_split[[2]]

We begin the assignment of labels to the buidings: blighted or not blighted. Buildings will be represented by parcels that have or have had buildings on them, whether by being so represented as in the parcels_sf data frame as including structures or in the dismantle permits dataframe as having had a dismante permit associated with it, thus suggesting that there was a building on the parcel.

We will use parcel numbers to refer to the parcels. However, as the following bit of code shows, the parcels dataset contains a few rows in which the parcel humbers are the same (duplicate_parcel_numbers_in_parcel_data contains 78 rows).

parcel_sf <- parcel_sf %>%
  mutate(row_num = row_number())
#As per above, following returns a 78-row data frame
duplicate_parcel_numbers_in_parcel_data <- 
  parcel_sf %>%
  group_by(parcelnum) %>% 
  mutate(n = n()) %>%
  ungroup() %>% 
  filter(n > 1) %>%
  select(parcelnum, address, legaldesc, row_num)
duplicate_parcel_numbers_in_parcel_data
Simple feature collection with 78 features and 4 fields
geometry type:  MULTIPOLYGON
dimension:      XY
bbox:           xmin: -83.17034 ymin: 42.32219 xmax: -83.0055 ymax: 42.4416
epsg (SRID):    4326
proj4string:    +proj=longlat +ellps=WGS84 +no_defs

Plotting the parcel data shows that, within groups of parcels that have the same parcel number, some of the pacrcels are geometrically identical while others are disjoint. We thus a apply a spatial join, using sf::st_join, to find, among each of these groups of parcels, the parcels with different parcel numbers but which are geometrically the same.

#join the result with itself, on the basis of sameness of identity of spatial identity 
spatial_repeats <- st_join(duplicate_parcel_numbers_in_parcel_data,
                           duplicate_parcel_numbers_in_parcel_data, 
                           st_equals, left = FALSE) %>% filter(row_num.x != row_num.y)
#select one element from each group with the sf objects for which the polygon covers the same area
selection_vector <- 1:nrow(spatial_repeats)
for (index in 1:nrow(spatial_repeats)) {
  selection_vector[index] <- !(spatial_repeats$row_num.x[index] %in% spatial_repeats$row_num.y[1:index]) 
}
selection_vector <- as.logical(selection_vector)
#the unique parcels, as described in unique_parcels
unique_parcels <- spatial_repeats[selection_vector,]
#the unique parcels, as described in parcel_sf, with a row number added to the end of the parcel number (character vector)
unique_parcels <- parcel_sf %>% filter(row_num %in% unique_parcels$row_num.x) %>%
  mutate(parcelnum = str_c(parcelnum, "_", row_number()))
#cut out the set of sf objects that have spatial repeats  
parcel_sf <- parcel_sf %>% filter(!(row_num %in% spatial_repeats$row_num.x))
#bind the the set of unique spatial objects to parcel_sf
parcel_sf <- parcel_sf %>% rbind(unique_parcels)
#check for overlap of the parcels in parcel_sf
spatial_repeats <- st_join(parcel_sf, parcel_sf, 
        st_overlaps, left = FALSE) %>% filter(row_num.x != row_num.y)
although coordinates are longitude/latitude, st_overlaps assumes that they are planar
#create vectors or row numbers in parcel_sf whose corresponding elements specify objects in parcel_sf with overlapping geometries.
x_rows <- spatial_repeats$row_num.x
y_rows <- spatial_repeats$row_num.y
#examine a random selection of these cases
set.seed(55); sample <- sample(1:nrow(spatial_repeats), 20)
parcel_selection <- spatial_repeats[sample,] %>% select(row_num.x, row_num.y)
plots <- map(1:20, function(index){
  row_1 <- (parcel_sf %>% select(parcelnum))[parcel_selection$row_num.x[index],]
  row_2 <- (parcel_sf %>% select(parcelnum))[parcel_selection$row_num.y[index],]
  plot(rbind(row_1, row_2))
})

#remove the row_num variable from pracel_sf
parcel_sf <- parcel_sf %>% select(-row_num)
rm(duplicate_parcel_numbers_in_parcel_data, selection_vector, unique_parcels)

The dismantle permits data also contains some duplicate parcel numbers over rows, in some cases over rows that contain address information suggesting that the location is different. (It also indicates that a few individual locatations, identified with addresses, had more than one associated dismantle permit. This need not be problematic—a permit could expire before the work is carried out, or there could be more than one structure on a parcel.) These repetitions are in duplicate_parcel_numbers_over_distinct_addresses, which contains 260 rows. I should also note that, in most of these cases with duplicate parcel numbers (and addresses indicating different locations), the recorded latitude and longitude are identical. We can thus infer that some of this location information is incorrect.

#repeated parcel numbers in the dismantle permits data
dup_par_num_in_dismantle_data <-
  dismantle_permits_sf %>%
  group_by(`Parcel Number`) %>% 
  mutate(n = n()) %>%
  ungroup() %>% 
  filter(n > 1) %>%
  select(`Parcel Number`, `Site Address`) %>%
  arrange(`Parcel Number`)
rm(dup_par_num_in_dismantle_data)
#parcel numbers in the dismantle permits data that are distributed over disinct address strings
dup_par_num_over_distinct_addresses <- 
  dismantle_permits_sf %>%
  group_by(`Parcel Number`) %>%
  mutate(parcel_number_occurances = n()) %>%
  ungroup() %>%
  filter(parcel_number_occurances > 1) %>%
  group_by(`Parcel Number`, `Site Address`) %>%
  mutate(m = n()) %>%
  filter(m < parcel_number_occurances) %>%
  arrange(`Parcel Number`)
#view selected portions of dup_par_num_over_distinct_addresses
as.data.frame(dup_par_num_over_distinct_addresses) %>%
  select(`Parcel Number`, `Site Address`, parcel_number_occurances, m)
#remove extraneous variables and then geocode the dismantle site addresses
geocoded_duplicates <- dup_par_num_over_distinct_addresses %>%
  as.data.frame %>%
  select(-parcel_number_occurances, -m, -geometry) %>%
  mutate(location = map(str_c(`Site Address`, ", Detroit, Michigan"), geocode_pause))
  
#avoid having to do the geocoding again when re-running the code
write_rds(geocoded_duplicates, "./data/geocoded_duplicates.rds")  

Having identified some potentially problematic data points, we need to investigate a few of them. As in the following example.

temp1 <- read_rds("./data/geocoded_duplicates.rds")
#Isolate some variables for viewing
temp2 <- temp1 %>% select(`Parcel Number`, location, `Permit Location`)
#eleven seeminly separate locations with the same listed parcel number
temp3 <- temp1 %>% filter(`Parcel Number` == "13006809.")
#put into an sf form
temp4 <- st_as_sf(unnest(temp3), coords = c("lon", "lat"), crs = st_crs(parcel_sf))
#function for putting a set of of sf point on a satelite map
sf_points_map <- function(sf_df) {
  df <- sf_df %>% mutate(longitude = st_coordinates(sf_df)[,1],
                                latitude = st_coordinates(sf_df)[,2]) %>%
  as.data.frame %>% select(longitude, latitude)
  
  #left/bottom/right/top for bounding box
  bounding_box <- c(min(df$longitude) - 0.002, min(df$latitude) - 0.002, 
                    max(df$longitude) + 0.002, max(df$latitude) + 0.002)
  detroit_gg <- get_map(location = bounding_box, 
                        maptype = "satellite")
  ggmap(detroit_gg) + geom_point(data = df, aes(x = longitude, y = latitude)) 
}
#map of points with Parcel Number listed as "13006809."
sf_points_map(temp4)
converting bounding box to center/zoom specification. (experimental)
Map from URL : http://maps.googleapis.com/maps/api/staticmap?center=42.410747,-83.043346&zoom=17&size=640x640&scale=2&maptype=satellite&language=en-EN&sensor=false

#It is also notable that "13006809." is not reflected as a parcel number in parcel_sf (noting that parcel_sf$parcelnum is an integer vector)
parcel_sf %>% filter(parcelnum == as.integer("13006809."))
Simple feature collection with 0 features and 37 fields
bbox:           xmin: NA ymin: NA xmax: NA ymax: NA
epsg (SRID):    4326
proj4string:    +proj=longlat +ellps=WGS84 +no_defs
 [1] sev        building_s landmap    pre        taxable_st last_sale  last_sa_01
 [8] objectid   num_buildi floor_area owner1     parcelnum  sqft       owner_coun
[15] land_value legaldesc  related_pa zoning     nez        address    owner2    
[22] total_acre taxable_va owner_city depth      year_built last_terms improved_v
[29] owner_stat taxpayer   council_di owner_zip  zip_code   property_c frontage  
[36] owner_stre ward       geometry  
<0 rows> (or 0-length row.names)
#try a spacial join to see where these points in the dismantled dataset hook up with the parcels dataset
temp5 <- st_join(parcel_sf, temp4, left = FALSE, st_contains)
although coordinates are longitude/latitude, st_contains assumes that they are planar
temp5$`Parcel Number`
[1] "13006809." "13006809." "13006809."
#three rows, all with `Parcel Number` = "13006809."
#plot with crs = 3857 to match google maps
sf_points_map(temp4) +  
  geom_sf(data = temp5[1,] %>% select(geometry), crs = 3857, inherit.aes = FALSE)
converting bounding box to center/zoom specification. (experimental)
Map from URL : http://maps.googleapis.com/maps/api/staticmap?center=42.410747,-83.043346&zoom=17&size=640x640&scale=2&maptype=satellite&language=en-EN&sensor=false
Coordinate system already present. Adding new coordinate system, which will replace the existing one.

rm(temp1, temp2, temp3, temp4, temp5)

We thus see that at least some of the parcels contain multiple structures. It is also notable that some of the positions associated with the dismantle permits, particularly those in the streets, are far enough from the the parcel to potentially be closer to other parcels. In particular, investigation of the surrounding area with google earth suggests that the point in temp4 that is the furthest south is quite close to a dismntled building that is well-outside of the parcel.

We resume the development our our model.

#read in the saved set of examples that involve repeats of parcel numbers over different addresses.
dismantle_duplicates_geocoded <- read_rds("./data/geocoded_duplicates.rds")
#unpack the column of data frames (the outputs of geocode())
dismantle_duplicates_geocoded <- remove_null_locations(dismantle_duplicates_geocoded) %>%
   unnest(location)
#note the 16 rows of dismantle_duplicates_geocoded for which another row lists the same longitude and latitude
dismantle_duplicates_geocoded %>% 
  group_by(lon, lat) %>% mutate(n = n()) %>% filter(n > 1)
  
#keep only the first of any group of rows with the same longitude and latitude
dismantle_duplicates_geocoded <-
  dismantle_duplicates_geocoded %>% distinct(lon, lat, .keep_all = TRUE)

Using Google Street View to investigate some more dismantle permit entries for which there are other entries with the same parcel number but different addresses, we find mostly different locations at the same buidling (e.g. the two sides of a duplex), or perhaps two attached buildings.

Continuing our construction:

#manually cut out the one remaining apparent duplicate location
dismantle_duplicates_geocoded <- dismantle_duplicates_geocoded %>% 
  filter(!(`Site Address` == "3200 E LAFAYETTE-MARTIN LUTHER KING HIGH"))

dismantle_duplicates_geocoded <- dismantle_duplicates_geocoded %>%
  st_as_sf(coords = c("lon", "lat"), crs = 4326)

#It is odd that this step is necessary, given that CRSs of the two data frames appear to be the same
st_crs(dismantle_duplicates_geocoded) <- st_crs(parcel_sf)

#where possible, find the parcels that contain the positions corresponding to the coordinates
spacial_join_within <- 
  st_join(dismantle_duplicates_geocoded, 
          parcel_sf %>% select(parcelnum), 
          st_within)

#change the parcel numbers (for the dismantle permits data) to those for the parcels that contain the parcels specified by the coordinates. remove the parcelnum column (from the parcel_sf data frame)
dismantle_duplicates_geocoded <- spacial_join_within %>%
  mutate(`Parcel Number` = ifelse(!is.na(parcelnum),
                                  as.character(parcelnum),
                                  `Parcel Number`)) %>%
  select(-parcelnum)

#remove from dismantle_permits_sf all of the rows with repeated parcel numbers and then
#replace then, when possible, with geocoded rows. first make the CRSs consistent (with the observation, again, that they would appear to already be the so).
st_crs(dismantle_duplicates_geocoded) <- st_crs(dismantle_permits_sf)

#PERHAPS CHANGE THIS...
dismantle_permits_sf <- dismantle_permits_sf %>% 
  filter(!(`Parcel Number` %in% dup_par_num_over_distinct_addresses$`Parcel Number`)) %>%
  rbind(dismantle_duplicates_geocoded)

rm(dismantle_duplicates_geocoded, dup_par_num_over_distinct_addresses)

We now begin the process of creating a set of labels over the buildings. The first step will be to create a dataframe in which each row is a “building”—the set parcels (elements of parcel_sf) such that each either (1) has a building on it, as indicated in parcel_sf, or (2) a dismantle permit for the same location as the parcel. I will begin with the identification of the buildings.

buildings <- parcel_sf #%>% 

typeof(parcel_sf$parcelnum)
typeof(dismantle_permits_sf$`Parcel Number`)
parcel_join_sf_parcel_first <- 
  inner_join(parcel_sf %>% select(parcelnum) %>% mutate(parcelnum = as.character((parcelnum))),
            dismantle_permits_sf %>% as.data.frame() %>% select(`Parcel Number`),
            c("parcelnum" = "Parcel Number")) %>%
  arrange(parcelnum)

parcel_join_sf_dismantle_first <-
  full_join(dismantle_permits_sf %>% select(`Parcel Number`), 
            parcel_sf %>% as.data.frame() %>% select(parcelnum),
            c("Parcel Number" = "parcelnum")) %>%
  arrange(`Parcel Number`)

st_crs(parcel_sf)
st_crs(dismantle_permits_sf)

#Both of these yield an EPSG of 4326. Nethereless, the follow step seems necessary.

st_crs(dismantle_permits_sf) <- st_crs(parcel_sf)

temp <- st_join(dismantle_permits_sf, parcel_sf, join = st_within)

temp2 <- inner_join(temp, as.data.frame(parcel_join_sf_parcel_first), by = "parcelnum")
  
temp2 <- temp %>% filter(`Parcel Number` != parcelnum)

temp2 <- temp2 %>% select(`Parcel Number`, parcelnum, `Site Address`, address)


#note that objectid in parcel_sf has no NA values, and that objectid is a key in parcel_sf
parcel_sf %>% as.data.frame() %>% filter(is.na(objectid))
parcel_sf %>% count(objectid) %>% filter(n > 1)

#note that `Permit Number` is a also a key, for dismantle_permits_sf
dismantle_permits_sf %>% as.data.frame() %>% filter(is.na(`Permit Number`))
dismantle_permits_sf %>% count(`Permit Number`) %>% filter(n > 1)

#with the above in mind, we examine the NA values for these columns in our full join, beginning with objectid in parcel_sf. The idea is that any NA value for objectid in parcel_join would correspond to a parcel in the dismantle permits dataset that is either not reflected in the parcels dataset or for which there is a flaw in the data.
parcel_join %>% filter(is.na(objectid))

dismantle_permits_sf %>% filter(`Parcel Number` == "22108512.")

#dismantled_structures <- initial_join %>% filter(!is.na(`Permit Number`))

parcel_sf %>% filter(parcelnum == "22108512.")

parcel_tmp <- blight_violations_sf[c(1, 2),]

st_distance(parcel_tmp)

m_list <- mapply(st_distance, blight_violations_sf$geometry[1], fred$geometry)

dismantle_permits_sf %>% as.data.frame() %>% filter(is.na(`Parcel Number`))

temp4 <- st_distance(parcel_sf[195099,], 
            blight_violations_sf[22233:22255,])

st_crs(blight_violations_sf) <- st_crs(parcel_sf)

plot(blight_violations_sf, max.plot = 1)

Maps:

library(ggmap)

ggplot(blight_violations_sf %>% select(geometry)) +
  geom_sf() + 
  geom_density2d(data = blight_violations_sf %>% select(geometry))


detroit_gg <- get_map("Detroit", maptype = "toner-lite",
                      zoom = 11)

temp <- get_map()

ggmap(detroit_gg)
LS0tCnRpdGxlOiAiQmxpZ2h0IFByZWRpY3Rpb25zIGZvciBEZXRyb2l0IgpvdXRwdXQ6IGh0bWxfbm90ZWJvb2sKLS0tCgpJbiB0aGlzIHByb2plY3QsIEkgd2lsbCBhdHRlbXB0IHRvIHByZWRpY3Qgd2hldGhlciwgZm9yIGFueSBnaXZlbiBwYXJjZWwgaW4gRGV0cm9pdCB3aXRoIGEgYnVpbGRpbmcgb24gaXQsIHdoZXRoZXIgYSBidWlsZGluZyBvbiB0aGF0IHBhcmNlbCB3aWxsIGJlIGJlIHRhcmdldHRlZCBmb3IgZGVtb2x0aW9uLiBQb3RlbnRpYWwgcHJlZGljdG9ycyBpbmNsdWRlIGNpdGF0aW9ucyByZWxhdGF0ZWQgdG8gdGhlIGJ1aWxkaW5nLCBjcmltZSwgYW5kIGNvbXBsYWludHMgY29uY2VybmluZyB0aGUgYnVpbGRpbmcgcmVsYXRlZCB0byBibGlnaHQuIFRoZSBwcm9iamVjdCBpcyBjdXJyZW50bHkgYXQgdGhlIGRhdGEgY2xlYW5pbmcgc3RhZ2UsIGFuZCBJIG1heSBhZGQgc29tZSBvdGhlciBkYXRhIHRvIHRoZSBwcm9qZWN0LiBTbyBmYXIgSSBhbSB1c2luZyBhYm91dCAzR0Igb2YgZGF0YSBkb3dubG9hZGVkIGZyb20gYGh0dHBzOi8vZGF0YS5kZXRyb2l0bWkuZ292L2AuCgoKYGBge3IsIG1lc3NhZ2UgPSBGQUxTRSwgd2FybmluZyA9IEZBTFNFLCByZXN1bHRzID0gRkFMU0V9CmxpYnJhcnkodGlkeXZlcnNlKQpsaWJyYXJ5KHNmKQpsaWJyYXJ5KGdnbWFwKQpsaWJyYXJ5KGx3Z2VvbSkKCiNyZWNvcmRlZCB2aW9sYXRpb25zIGFzc29jaWF0ZWQgd2l0aCBibGlnaHQgKGUuZy4gdW5rZW1wdCBwcm9wZXJ0aWVzKQpibGlnaHRfdmlvbGF0aW9ucyA8LSByZWFkX2NzdigiLi9kYXRhL0JsaWdodF9WaW9sYXRpb25zXzNfMTlfMjAxOC5jc3YiLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgZ3Vlc3NfbWF4ID0gMTBeNikKCiNyZWFkIHRoZSBkb3dubG9hZGVkIGZpbGUgZm9yIGFsbCB0aGUgYnVpbGRpbmcgcGVybWl0cyBhbmQgdGhlbiBmaWx0ZXIgb3V0IHRoZSBwZXJtaXRzIGZvciBkaXNtYW50bGluZwpkaXNtYW50bGVfcGVybWl0cyA8LSByZWFkX2NzdigiLi9kYXRhL0J1aWxkaW5nX1Blcm1pdHNfM18xOV8yMDE4LmNzdiIsIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgIGd1ZXNzX21heCA9IDEwXjYpICU+JQogIGZpbHRlcihgQnVpbGRpbmcgUGVybWl0IFR5cGVgID09ICJEaXNtYW50bGUiKQogIAojdGhlIGZpbGVzIHRoYXQgY29udGFpbiB0aGUgY3JpbWUgZGF0YQpjcmltZV90b18xMjA2MjAxNiA8LSAKICByZWFkX2NzdigiLi9kYXRhL0RQRF9fQWxsX0NyaW1lX0luY2lkZW50c19fSmFudWFyeV8xX18yMDA5Xy1fRGVjZW1iZXJfNl9fMjAxNi5jc3YiLAogICAgICAgICAgIGd1ZXNzX21heCA9IDEwXjYpCgpjcmltZV8xMjA2MjAxNl90b18wMzE5MjAxOCA8LSAKICByZWFkX2NzdigiLi9kYXRhL0RQRF9fQWxsX0NyaW1lX0luY2lkZW50c19fRGVjZW1iZXJfNl9fMjAxNl8tXzNfMTlfMjAxOC5jc3YiLCAKICAgICAgICAgICBndWVzc19tYXggPSAxMF42KQoKI3RoZSAzMTEgc3lzdGVtCmltcHJvdmVfZGV0cm9pdF9pc3N1ZXMgPC0gcmVhZF9jc3YoIi4vZGF0YS9JbXByb3ZlX0RldHJvaXRfSXNzdWVzXzNfMTlfMjAxOC5jc3YiLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBndWVzc19tYXggPSAxMF42KQoKI2Fub3RoZXIgZmlsZSB3aXRoIGRlbW9saXRpb24gaW5mb3JtYXRpb24gZG93bmxvYWRlZCA0LzQvMjAxNwpjb21wbGV0ZWRfZGVtb2xpdGlvbnMgPC0gcmVhZF9jc3YoIi4vZGF0YS9EZXRyb2l0X0RlbW9saXRpb25zLmNzdiIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBndWVzc19tYXggPSAxMF42KQoKI3RoZSBzaGFwZWZpbGUgcmVwcmVzZW50aW5nIERldHJvaXQgcGFyY2VscywgcmVhZCBpbnRvIApwYXJjZWxfc2YgPC0gc3RfcmVhZCgiLi9kYXRhL1BhcmNlbCBNYXAiKQoKI2NvbnZlcnQgdGhlIHBhcmNlbCBudW1iZXIgY29sdW1uIHRvIGEgY2hhcmFjdGVyIHZlY3RvciwgdG8gbWF0Y2ggYFBhcmNlbCBOdW1iZXJgIGluIHRoZSBkaXNtYW50bGUgcGVybWl0cyBkYXRhCnBhcmNlbF9zZiA8LSBwYXJjZWxfc2YgJT4lIG11dGF0ZShwYXJjZWxudW0gPSBhcy5jaGFyYWN0ZXIocGFyY2VsbnVtKSkKCmBgYAoKRm9yIGFsbCBvZiB0aGUgZG93bmxvYWRlZCBkYXRhc2V0cyBvdGhlciB0aGFuIHRoZSBwYXJjZWxzIGRhdGFzZXQsIHdlIGV4dHJhY3QgdGhlIHVzYWJsZSBsYXR1dHVkZSBhbmQgbG9uZ2l0dWRlIHZhbHVlcyBhbmQgdGhlbiB1c2UgdGhpcyBpbmZvcm1hdGlvbiB0byBmb3JtIHNpbXBsZSBmZWF0dXJlcyAoc2YpIG9iamVjdHMuIFJvd3Mgd2l0aCBvYnZpb3VzbHkgaW5jb3JyZWN0IHZhbHVlcywgb3IgdmFsdWVzIHRoYXQgd291bGQgcmVwcmVzZW50IHBvc2l0aW9ucyB3ZWxsIG91dHNpZGUgRGV0cm9pdCwgYXJlIGZpbHRlcmVkIG91dCwgdG9nZXRoZXIgd2l0aCByb3dzIGZvciB3aGljaCB0aGUgbGF0aXR1ZGUgb3IgbG9uZ2l0dWRlIGRhdGEgaXMgbWlzc2luZy4KCmBgYHtyfQoKI2Z1bmN0aW9uIGZvciBjb252ZXJ0aW5nIHRoZSBwb3NpdGlvbiAoY2hhcmFjdGVyKSBjb2x1bW4gaW50byBhIGNvbHVtbiBvZiBwb2ludHMgaW4gdGhlIHNpbXBsZSBmZWF0dXJlcyAoc2YpIGZyYW1ld29yay4KYWRkX3NmX3BvaW50IDwtIGZ1bmN0aW9uKGRmLCBjb2x1bW4pIHsKICAKICAjZXh0cmFjdCB0aGUgbGF0aXR1ZGUgYW5kIGxvbmdpdHVkZSBmcm9tIHRoZSBzdHJpbmcgY29sdW1uIHRoYXQgY29udGFpbnMgYm90aC4gV2l0aCB0aGUgcGFyZW50aGVzZXMKICAjbG9jYXRlZCBmcm9tIHRoZSBlbmQgb2YgdGhlIHN0cmluZ3MsIGl0IGlzIHBvc3NpYmxlIHRvIHVzZSB0aGUgdGhlIHNhbWUgZnVuY3Rpb24gZm9yIGFsbCBmaXZlIG9mIAogICN0aGUgZGF0YXNldHMgZm9yIHdoaWNoIHdlIG5lZWQgdG8gZXh0cmFjdCB0aGlzIGluZm9ybWF0aW9uLgogIGxhdGl0dWRlIDwtIHN0cl9zdWIoZGZbW2NvbHVtbl1dLCAKICAgICAgICAgICAgICAgICAgICAgIHN0cmluZ2k6OnN0cmlfbG9jYXRlX2xhc3RfZml4ZWQoZGZbW2NvbHVtbl1dLCAiKCIpWywyXSArIDEsCiAgICAgICAgICAgICAgICAgICAgICBzdHJpbmdpOjpzdHJpX2xvY2F0ZV9sYXN0X2ZpeGVkKGRmW1tjb2x1bW5dXSwgIiwiKVssMV0gLSAxKQogIGxvbmdpdHVkZSA8LSBzdHJfc3ViKGRmW1tjb2x1bW5dXSwgCiAgICAgICAgICAgICAgICAgICAgICAgc3RyaW5naTo6c3RyaV9sb2NhdGVfbGFzdF9maXhlZChkZltbY29sdW1uXV0sICIsICIpWywyXSArIDEsIAogICAgICAgICAgICAgICAgICAgICAgIHN0cmluZ2k6OnN0cmlfbG9jYXRlX2xhc3RfZml4ZWQoZGZbW2NvbHVtbl1dLCAiKSIpWywxXSAtIDEpCiAgCiAgI2FkZCB0aGUgbGF0aXRpdHVkZSBhbmQgYW5kIGxvbmdpdHVkZSB0byBhIGNvcHkgb2YgdGhlIGRhdGFmcmFtZSwgZmlsdGVyIG91dCB0aGUgTkFzIGZyb20KICAjdGhlc2UgcmVzdWx0cywgYW5kIHRoZW4gY29udmVydCBjb252ZXJ0IHRvIHNmLCB3aXRoIHBvaW50IHBvc2l0aW9ucyBpbmRpY2F0ZWQgaW4gdGhlCiAgI2dlb21ldHJ5IGNvbHVtbgogIG11dGF0ZWQgPC0gZGYgJT4lIG11dGF0ZShleHRyYWN0ZWRfbGF0ID0gYXMuZG91YmxlKGxhdGl0dWRlKSwKICAgICAgICAgICAgICAgICAgICAgIGV4dHJhY3RlZF9sb24gPSBhcy5kb3VibGUobG9uZ2l0dWRlKSkKICAKICAjcmVtb3ZlIHJvd3Mgd2l0aCBOQXMgZm9yIGxhdGl0dWRlIG9yIGxvbmdpdHVkZSwgb3Igd2l0aCB2YWx1ZXMgd2VsbCBvdXRzaWRlIG9mIERldHJvaXQKICBmaWx0ZXJlZCA8LSBtdXRhdGVkICU+JQogICAgZmlsdGVyKCFpcy5uYShleHRyYWN0ZWRfbGF0KSAmICFpcy5uYShleHRyYWN0ZWRfbG9uKSkgJT4lCiAgICBmaWx0ZXIoNDEgPCBleHRyYWN0ZWRfbGF0ICYgZXh0cmFjdGVkX2xhdCA8IDQ0ICYgLTg1IDwgZXh0cmFjdGVkX2xvbiAmIGV4dHJhY3RlZF9sb24gPCAtODEpCiAgICAKICAjY3JlYXRlIGEgZGF0YWZyYW1lIGZyb20gdGhlIGl0ZW1zIHRoYXQgaGF2ZSBiZWVuIGZpbHRlcmVkIG91dAogIHJlc3VsdF9jb29yZF9uYSA8LSBzZXRkaWZmKG11dGF0ZWQsIGZpbHRlcmVkKQogIAogICNjcmVhdGUgc2Ygb2JqZWN0cyBmcm9tIHRoZSByb3dzIHdpdGggdXNhYmxlIGxhdGl0dWRlIGFuZCBsb25naXR1ZGUgaW5mb3JtYXRpb24KICByZXN1bHRfc2YgPC0gc3RfYXNfc2YoZmlsdGVyZWQsIGNvb3JkcyA9IGMoImV4dHJhY3RlZF9sb24iLCAiZXh0cmFjdGVkX2xhdCIpLCBjcnMgPSA0MzI2KQogICAgCiAgcmV0dXJuKGxpc3QocmVzdWx0X3NmLCByZXN1bHRfY29vcmRfbmEpKQp9CgojYXBwbHkgdGhlIGZ1bmN0aW9uIHRvIHRoZSBmaXZlIGRhdGFzZXRzIGZvciB3aGljaCB0aGUgZGF0YSB3YXMgbm90IGxvYWRlZCBhcyBhIHNpbXBsZSBmZWF0dXJlcyBkYXRhZnJhbWUsIHRodXMgcHJvZHVjaW5nIGEgbGlzdCBvZiB0d28gZGF0YWZyYW1lcyBmb3IgZWFjaCBvZiB0aGUgZGF0YXNldHMsIHRoZSBmaXJzdCBlbGVtZW50IG9mIHRoZSBsaXN0IGEgc2ltcGxlIGZlYXR1cmVzIGRhdGEgZnJhbWUgYW5kIHRoZSBzZWNvbmQgZWxlbWVudCBhIGRhdGFmcmFtZSB3aXRoIHRoZSBpbnN0YW5jZXMgZm9yIHdoaWNoIGl0IHdhcyBub3QgcG9zc2libGUgdG8gY29udmVydCB0byBzaW1wbGUgZmVhdHVyZXMKYmxpZ2h0X3Zpb2xhdGlvbnNfc3BsaXQgPC0gYWRkX3NmX3BvaW50KGJsaWdodF92aW9sYXRpb25zLCAiVmlvbGF0aW9uIExvY2F0aW9uIikKCmRpc21hbnRsZV9wZXJtaXRzX3NwbGl0IDwtIGFkZF9zZl9wb2ludChkaXNtYW50bGVfcGVybWl0cywgIlBlcm1pdCBMb2NhdGlvbiIpCgpjcmltZV90b18xMjA2MjAxNl9zcGxpdCA8LSBhZGRfc2ZfcG9pbnQoY3JpbWVfdG9fMTIwNjIwMTYsICJMT0NBVElPTiIpCgpjcmltZV8xMjA2MjAxNl90b18wMzE5MjAxOF9zcGxpdCA8LSBhZGRfc2ZfcG9pbnQoY3JpbWVfMTIwNjIwMTZfdG9fMDMxOTIwMTgsICJMb2NhdGlvbiIpCgppbXByb3ZlX2RldHJvaXRfaXNzdWVzX3NwbGl0IDwtIGFkZF9zZl9wb2ludChpbXByb3ZlX2RldHJvaXRfaXNzdWVzLCAiTG9jYXRpb24iKQoKY29tcGxldGVkX2RlbW9saXRpb25zX3NwbGl0IDwtIGFkZF9zZl9wb2ludChjb21wbGV0ZWRfZGVtb2xpdGlvbnMsICJMb2NhdGlvbiIpCgpgYGAKCldlIG5vdyBjb25zaWRlciB0aGUgZGF0YSBmb3Igd2hpY2ggd2UgZG8gbm90IHlldCBoYXZlIHBvc2l0aW9uIGRhdGEsIGFuZCBjb21wbGV0ZSB0aGUgaW5mb3JtYXRpb24gYXMgd2VsbCBhcyB3ZSByZWFzb25hYmx5IGNhbiwgdXNpbmcgdGhlIEdvb2dsZSBhcGkgYW5kIGEgZnVuY3Rpb24sIGBnZW9jb2RlX3BhdXNlYCwgdGhhdCBoYW5kbGVzIHNvbWUgb2YgYXBpJ3MgcXVpcmtzLgoKYGBge3J9CgojdGhlIHBvcnRpbm8gb2YgdGhlIGRvd25sb2FkZWQgYmxpZ2h0IGNpdGF0aW9ucyBkYXRhLCBmb3Igd2hpY2ggd2UgZG8gbm90IGhhdmUKYmxpZ2h0X3Zpb19uYSA8LSBibGlnaHRfdmlvbGF0aW9uc19zcGxpdFtbMl1dCgojcmVtb3ZlIHRoZSByb3dzIGZvciB3aGljaCBnZW9jb2RpbmcgaXMgbm90IGxpa2VseSB0byBwcm9kb2NlIHJlbGlhYmxlIHJlc3VsdHMKdXNlZnVsIDwtIGJsaWdodF92aW9fbmEgJT4lIGZpbHRlcighaXMubmEoYFZpb2xhdGlvbiBTdHJlZXQgTmFtZWApLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBgVmlvbGF0aW9uIFN0cmVldCBOdW1iZXJgID4gMCwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIWlzLm5hKGBWaW9sYXRpb24gWmlwIENvZGVgKSkKCiNjcmVhdGUgYSBjb2x1bW4gb2YgYWRkcmVzc2VzIHRvIGJlIHVzZWQgaW4gZ2VvY29kaW5nCnVzZWZ1bCA8LSB1c2VmdWwgJT4lIAogIG11dGF0ZShjb21wbGV0ZV9hZGRyZXNzID0gcGFzdGUoYFZpb2xhdGlvbiBTdHJlZXQgTnVtYmVyYCwgIiAiLCBgVmlvbGF0aW9uIFN0cmVldCBOYW1lYCwgIiwgIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJEZXRyb2l0LCBNaWNoaWdhbiIsICIgIiwgYFZpb2xhdGlvbiBaaXAgQ29kZWAsIHNlcCA9ICIiKSkKCiNmdW5jdGlvbiBtYWtlcyBhIG1heGltdW0gNiBhdHRlbXB0cyB0byBnZW9jb2RlIHRoZSBnaXZlbiBhZGRyZXNzIHVzaW5nIHRoZSBHb29nbGUgQVBJLCB3aXRoIGEgcGF1c2Ugb2YgMSBzZWNvbmQgYmV0d2VlbiBhdHRlbXB0cy4gV2Ugd2lsbCB1c2UgdGhlIGZ1bmN0aW9uIGZvciB0aGUgb3RoZXIgZGF0YXNldHMgYXMgd2VsbC4KZ2VvY29kZV9wYXVzZSA8LSBmdW5jdGlvbihhZGRyZXNzKSB7CiAgZm9yIChpbmRleCBpbiAxOjYpIHsKICAgIFN5cy5zbGVlcCgxKQogICAgbG9jYXRpb24gPC0gZ2dtYXA6Omdlb2NvZGUoYWRkcmVzcykKICAgIGlmICghaXMubmEobG9jYXRpb24kbG9uKSkgewogICAgICByZXR1cm4obG9jYXRpb24pCiAgICB9CiAgfQp9CmBgYAoKCmBgYHtyLCBldmFsID0gRkFMU0V9CiNhcHBseSBnZW9jb2RlX3BhdXNlIHRvIGVhY2ggb2YgdGhlIGVsZW1lbnRzIG9mIHRoZSBjb21wbGV0ZV9hZGRyZXNzZSBjb2x1bW4gYW5kIHBsYWNlIHRoZSByZXN1bHQgaW4gYSBuZXcgY29sdW1uLCBpbiB3aGljaCBlYWNoIGVudHJ5IGlzIGEgZGF0YSBmcmFtZSAKdXNlZnVsIDwtIHVzZWZ1bCAlPiUgbXV0YXRlKGxvY2F0aW9uID0gbWFwKGNvbXBsZXRlX2FkZHJlc3MsIGdlb2NvZGVfcGF1c2UpKQoKI3NhdmUgdG8gZGlzYywgdG8gYXZvaWQgYXZvaWQgdGhlIG5lZWQgdG8gZ2VvY29kZSB0aGVzZSBhZGRyZXNzZXMgYWdhaW4gd2hlbiB3ZSByZXJ1biB0aGUgYW5hbHlzaXMKd3JpdGVfcmRzKHVzZWZ1bCwgIi4vZGF0YS9ibGlnaHRfdmlvbGF0aW9uc19nZW9jb2Rlcy5yZHMiKQoKYGBgCgpUaGUgZ2VvY29kaW5nIGhhcyByZXR1cm5lZCBhIGRhdGEgZnJhbWUgZm9yIGVhY2ggb2YgdGhlIGFkZHJlc3Nlcy4gV2UgdGh1cyBuZWVkIHRvIHVucGFjayB0aGUgZWxlbWVudHMgb2YgdGhlIGxvY2F0aW9uIGNvbHVtbiwgZWFjaCBvZiB3aGljaCBpcyBhIGRhdGEgZnJhbWUuCgpgYGB7cn0KI3JlYWQgYmxpZ2h0X3Zpb2xhdGlvbnNfZ2VvY29kZXMgYXMgYSB0aWJibGUKYmxpZ2h0X3Zpb2xhdGlvbnNfZ2VvY29kZXMgPC0gcmVhZF9yZHMoIi4vZGF0YS9ibGlnaHRfdmlvbGF0aW9uc19nZW9jb2Rlcy5yZHMiKQoKI2Z1bmN0aW9uIGZvciByZW1vdmluZyB0aGUgaW5zdGFuY2VzIGZvciB3aGljaCBnZW9jb2RpbmcgZmFpbGVkIChmb3Igd2hpY2ggdGhlIHZhbHVlIGluIHRoZSBsb2NhdGlvbiBjb2x1bW4gaXMgTlVMTEwpLiBXZSB3aWxsIHVzZSB0aGlzIGZ1bmN0aW9uIGZvciBhbGwgb2YgdGhlIGdlb2NvZGVkIGRhdGEgZnJhbWVzLgpyZW1vdmVfbnVsbF9sb2NhdGlvbnMgPC0gZnVuY3Rpb24oZGYpIHsKICAjaWRlbnRpZnkgdGhlIHJvd3MgZm9yIHdoaWNoIHRoZSB2YWx1ZSBpbiB0aGUgbG9jYXRpb24gY29sdW1uIGlzIE5VTEwKICBudWxsX3Jvd3MgPC0gbGlzdCgpCiAgZm9yIChpbmRleCBpbiAxOm5yb3coZGYpKSB7CiAgICBpZiAoaXMubnVsbChkZiRsb2NhdGlvbltbaW5kZXhdXSkpIHsKICAgICAgbnVsbF9yb3dzIDwtIGMobnVsbF9yb3dzLCBpbmRleCkKICAgIH0KICB9CiAgI3JlbW92ZSB0aGUgcm93cyBmb3Igd2hpY2ggdGhlIHZhbHVlIG9mIHRoZSBsb2NhdGlvbiBjb2x1bW4gaXMgTlVMTAogIGRmIDwtIGRmWy1hcy5pbnRlZ2VyKG51bGxfcm93cyksXQp9CgpibGlnaHRfdmlvbGF0aW9uc19nZW9jb2RlcyA8LSByZW1vdmVfbnVsbF9sb2NhdGlvbnMoYmxpZ2h0X3Zpb2xhdGlvbnNfZ2VvY29kZXMpCgojV2l0aCBibGlnaHRfdmlvbGF0aW9uc19nZW9jb2RlcyBhIHRpYmJsZSwgd2UgY2FuIGFwcGx5IHRpZHlyOjp1bm5lc3QoKSwgd2hpY2ggd2lsbCBwbGFjZSB0aGUgbGF0aXR1ZGUgYW5kIGxvbmdpdHVkZSBpbiBjb2x1bW5zIGxhYmVsbGVkICJsYXQiIGFuZCAibG9uIi4KYmxpZ2h0X3Zpb2xhdGlvbnNfZ2VvY29kZXMgPC0gYmxpZ2h0X3Zpb2xhdGlvbnNfZ2VvY29kZXMgJT4lIHVubmVzdChsb2NhdGlvbikKCiNmaWxsIGluIHRoZSBgVmlvbGF0aW9uIExhdGl0dXRlYCBhbmQgYFZpb2xhdGlvbiBMb25naXR1ZGVgIGRhdGEgZnJhbWVzLCB3aGljaCBhbHJlYWQgZXhpc3QgaW4gdGhlIGJsaWdodF92aW9sYXRpb25zIGRhdGEgZnJhbWUKYmxpZ2h0X3Zpb2xhdGlvbnNfZ2VvY29kZXMgPC0gYmxpZ2h0X3Zpb2xhdGlvbnNfZ2VvY29kZXMgJT4lCiAgbXV0YXRlKGBWaW9sYXRpb24gTGF0aXR1ZGVgID0gbGF0LAogICAgICAgICBgVmlvbGF0aW9uIExvbmdpdHVkZWAgPSBsb24pCgojY3V0IG91dCBzb21lIGNvbHVtbnMgdGhhdCBoYXZlIGJlZW4gYWRkZWQKYmxpZ2h0X3Zpb2xhdGlvbnNfZ2VvY29kZXMgPC0gYmxpZ2h0X3Zpb2xhdGlvbnNfZ2VvY29kZXMgJT4lIAogIHNlbGVjdCgtZXh0cmFjdGVkX2xhdCwgLWV4dHJhY3RlZF9sb24sIC1jb21wbGV0ZV9hZGRyZXNzKQoKI3B1dCB0aGUgcG9zaXRpb24gaW5mb3JtYXRpb24gaW50byBhIHNpbXBsZSBmZWF0dXJlcyBmb3JtYXQgKHdoaWNoIHdpbGwgcmVtb3ZlIHRoZSAibGF0IiBhbmQgImxvbiIgY29sdW1ucykKYmxpZ2h0X3Zpb2xhdGlvbnNfZ2VvY29kZXNfc2YgPC0gc3RfYXNfc2YoYmxpZ2h0X3Zpb2xhdGlvbnNfZ2VvY29kZXMsIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBjb29yZHMgPSBjKCJsb24iLCAibGF0IiksCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGNycyA9IDQzMjYpCgojY29tYmluZSB0aGUgcmVzdWx0cyB3aXRoIHRoZSBwcmV2aW91c2x5IGdlbmVyYXRlZCBzZiBkYXRhCmJsaWdodF92aW9sYXRpb25zX3NmIDwtIHJiaW5kKGJsaWdodF92aW9sYXRpb25zX3NwbGl0W1sxXV0sIGJsaWdodF92aW9sYXRpb25zX2dlb2NvZGVzX3NmKQoKcm0oYmxpZ2h0X3Zpb19uYSwgYmxpZ2h0X3Zpb2xhdGlvbnMsIGJsaWdodF92aW9sYXRpb25zX2dlb2NvZGVzLCAKICAgYmxpZ2h0X3Zpb2xhdGlvbnNfZ2VvY29kZXNfc2YsIGJsaWdodF92aW9sYXRpb25zX3NwbGl0LCB1c2VmdWwpCgpgYGAKCmBgYHtyfQoKI3RoZSBkaXNtYW50bGUgcGVybWl0cyBmb3Igd2hpY2ggcG9zaXRpb24gZGF0YSAobGF0aXR1ZGUgYW5kIGxvbmdpdHVkZSkgaXMgbWlzc2luZwpkaXNtYW50bGVfcGVybWl0c19zcGxpdF9uYSA8LSBkaXNtYW50bGVfcGVybWl0c19zcGxpdFtbMl1dCgojcmVtb3ZlIHRoZSBsYXN0IHR3byBjb2x1bW5zLCB3aGljaCB3ZXJlIG5vdCBjb250YWluZWQgaW4gdGhlIG9yaWdpbmFsIGRpc21hbnRsZV9wZXJtaXRzIGRhdGFzdGV0CmRpc21hbnRsZV9wZXJtaXRzX3NwbGl0X25hIDwtIGRpc21hbnRsZV9wZXJtaXRzX3NwbGl0X25hICU+JSAKICBzZWxlY3QoLWV4dHJhY3RlZF9sYXQsIC1leHRyYWN0ZWRfbG9uKQoKYGBgCgoKYGBge3IsIGV2YWwgPSBGQUxTRX0KCiNnZW9jb2RlIHRoZSBpdGVtcyBpbiBkaXNtYW50bGVfcGVybWl0c19zcGxpdF9uYSwgdXNpbmcgdGhlIGFkZHJlc3MgY29sdW1uIGFuZCB0aGUgZnVuY3Rpb24gZ2VvY29kZV9wYXVzZSwgd2hpY2ggbWFrZXMgYSBtYXhpbXVtIG9mIHNpeCBhdHRlbXB0cyBmb3IgZWFjaCBpdGVtLiBUaGUgcmVzdWx0IGlzIGxpc3Qgb2YgZGF0YWZyYW1lcyBpbiB0aGUgbG9jYXRpb24gY29sdW1uLgpkaXNtYW50bGVfcGVybWl0c19zcGxpdF9nZW9jb2RlIDwtIGRpc21hbnRsZV9wZXJtaXRzX3NwbGl0X25hICU+JQogIG11dGF0ZShsb2NhdGlvbiA9IG1hcChzdHJfYyhgU2l0ZSBBZGRyZXNzYCwgIiwgRGV0cm9pdCwgTWljaGlnYW4iKSwgZ2VvY29kZV9wYXVzZSkpCgojd3JpdGUgdGhlIHJlc3VsdHMgb2YgdGhlIGdlb2NvZGluZyB0byBkaXNrLCB0byBhdm9pZCBoYXZpbmcgdG8gcmVwZWF0IHRoZSBnZW9jb2Rpbmcgd2hlbiByZXJ1bm5pbmcgdGhlIGFuYWx5c2lzLgp3cml0ZV9yZHMoZGlzbWFudGxlX3Blcm1pdHNfc3BsaXRfZ2VvY29kZSwgIi4vZGF0YS9kaXNtYW50bGVfcGVybWl0c19nZW9jb2Rlcy5yZHMiKQoKcm0oZGlzbWFudGxlX3Blcm1pdHNfc3BsaXRfbmEpCgpgYGAKCgpgYGB7cn0KI2xvYWQgdGhlIGdlb2NvZGVkIGRhdGEgZnJhbWUgaW50byBSCmRpc21hbnRsZV9wZXJtaXRzX3NwbGl0X2dlb2NvZGUgPC0gcmVhZF9yZHMoIi4vZGF0YS9kaXNtYW50bGVfcGVybWl0c19nZW9jb2Rlcy5yZHMiKQoKI3VzZSB0aGUgcmVtb3ZlX251bGxfbG9jYXRpb25zKCkgdG8gcmVtb3ZlIHRoZSByb3dzIGZvciB3aGljaCBnZW9jb2RpbmcgZmFpbGVkIGFuZCB0aGVuIHBhcnNlIHRoZSBpbmZvcm1hdGlvbiAgaW4gdGhlIGRhdGFmcmFtZXMgaW4gdGhlIGxvY2F0aW9uIGNvbHVtbiBpbnRvIHR3byBuZXcgY29sdW1ucywgbGF0IGFuZCBsYW4KZGlzbWFudGxlX3Blcm1pdHNfc3BsaXRfZ2VvY29kZSA8LSAKICByZW1vdmVfbnVsbF9sb2NhdGlvbnMoZGlzbWFudGxlX3Blcm1pdHNfc3BsaXRfZ2VvY29kZSkgJT4lCiAgdW5uZXN0KGxvY2F0aW9uKQoKI2NvbnZlcnQgdG8gYSBzaW1wbGUgZmVhdHVyZXMgKHNmKSBkYXRhIGZyYW1lLCB1c2luZyB0aGUgbGF0aXRpdHVkZXMgYW5kIGxvbmdpdHVkZXMKZGlzbWFudGxlX3Blcm1pdHNfZ2VvY29kZV9zZiA8LSBzdF9hc19zZihkaXNtYW50bGVfcGVybWl0c19zcGxpdF9nZW9jb2RlLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgY29vcmRzID0gYygibG9uIiwgImxhdCIpLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBjcnMgPSA0MzI2KQoKI2FwcGVuZCB0aGlzIHNpbXBsZSBmZWF0dXJlcyBkYXRhZnJhbWUgdG8gdGhlIGRhdGFmcmFtZSBmb3Igd2hpY2ggd2UgYWxyZWFkeSBoYWQgdXNhYmxlIHBvc2l0aW9ucwpkaXNtYW50bGVfcGVybWl0c19zZiA8LSByYmluZChkaXNtYW50bGVfcGVybWl0c19zcGxpdFtbMV1dLCBkaXNtYW50bGVfcGVybWl0c19nZW9jb2RlX3NmKQoKcm0oZGlzbWFudGxlX3Blcm1pdHNfc3BsaXRfZ2VvY29kZSwgZGlzbWFudGxlX3Blcm1pdHNfZ2VvY29kZV9zZiwgZGlzbWFudGxlX3Blcm1pdHMsIGRpc21hbnRsZV9wZXJtaXRzX3NwbGl0LCBkaXNtYW50bGVfcGVybWl0c19zcGxpdF9uYSkKCmBgYAoKV2Ugbm93IGZpbGwtaW4gdGhlIG1pc3NpbmcgcG9zaXRpb24gaW5mb3JtYXRpb24gZm9yIHRoZSBkYXRhc2V0IGZvciBjcmltZXMgdXAgdG8gMTItMDYtMjAxNgoKYGBge3J9CgojcmV0dXJuIHRvIHRoZSBvbGRlciBjcmltZSBkYXRhCmNyaW1lX3RvXzEyMDYyMDE2X2xlZnRvdmVycyA8LSBjcmltZV90b18xMjA2MjAxNl9zcGxpdFtbMl1dCgojY3V0IG91dCB0aGUgYWRkcmVzc2VzIHRoYXQgYmVnaW4gd2l0aCAiMDAiCmNyaW1lX3RvXzEyMDYyMDE2X2xlZnRvdmVycyA8LSBjcmltZV90b18xMjA2MjAxNl9sZWZ0b3ZlcnMgJT4lIAogIGZpbHRlcihzdHJfc3ViKExPQ0FUSU9OLCAxLCAyKSAhPSAiMDAiKQoKI2ZpbHRlciBvdXQgc29tZSBvYnZpb3VzbHkgdXNlbGVzcyBhZGRyZXNzZXMsIHdpdGggZmV3IGNoYXJhY3RlcnMgYmVmb3JlIHRoZSBmaXJzdCAiKCIKY3JpbWVfdG9fMTIwNjIwMTZfbGVmdG92ZXJzIDwtIGNyaW1lX3RvXzEyMDYyMDE2X2xlZnRvdmVycyAlPiUgCiAgZmlsdGVyKCFzdHJfbG9jYXRlKExPQ0FUSU9OLCAiXFwoIilbLDFdICVpbiUgMToxMykKCiNyZW1vdmUgdGhlIHR3byBjb2x1bW5zIHRoYXQgd2VyZSBhZGRlZCBlYXJsaWVyCmNyaW1lX3RvXzEyMDYyMDE2X2xlZnRvdmVycyA8LSBjcmltZV90b18xMjA2MjAxNl9sZWZ0b3ZlcnMgJT4lIAogIHNlbGVjdCgtZXh0cmFjdGVkX2xhdCwgLWV4dHJhY3RlZF9sb24pCgojY3JlYXRlIGEgY29sdW1uIGZvciB1c2UgaW4gZ2VvY29kaW5nCmNyaW1lX3RvXzEyMDYyMDE2X2xlZnRvdmVycyA8LSBjcmltZV90b18xMjA2MjAxNl9sZWZ0b3ZlcnMgJT4lIAogIG11dGF0ZShleHRyYWN0ZWRfYWRkcmVzcyA9IHN0cl9jKHN0cl9zdWIoTE9DQVRJT04sIDEsIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgc3RyX2xvY2F0ZShMT0NBVElPTiwgIlxcKCIpWywxXSAtIDIpLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiLCBEZXRyb2l0LCBNaWNoaWdhbiIpKQoKYGBgCgpgYGB7ciwgZXZhbCA9IEZBTFNFfQoKI2dlb2NvZGUgdGhlIGVsZW1lbnRzIG9mIGV4dHJhY3RlZF9hZGRyZXNzLCB1c2luZyB0aGUgZnVuY3Rpb24gZ2VvY29kZV9wYXVzZQpjcmltZV90b18xMjA2MjAxNl9sZWZ0b3ZlcnNfZ2VvY29kZSA8LSBjcmltZV90b18xMjA2MjAxNl9sZWZ0b3ZlcnMgJT4lIAogIG11dGF0ZShsb2NhdGlvbiA9IG1hcChleHRyYWN0ZWRfYWRkcmVzcywgZ2VvY29kZV9wYXVzZSkpCgojc2F2ZSB0aGUgcmVzdWx0cywgdG8gYXZvaWQgaGF2aW5nIHRvIGdlb2NvZGUgYWdhaW4gd2hlbiByZXJ1bm5pbmcgdGhlIGFuYWx5c2lzCndyaXRlX3JkcyhjcmltZV90b18xMjA2MjAxNl9sZWZ0b3ZlcnNfZ2VvY29kZSwgIi4vZGF0YS9jcmltZV90b18xMjA2MjAxNl9sZWZ0b3ZlcnNfZ2VvY29kZS5yZHMiKQoKYGBgCgoKCmBgYHtyfQoKY3JpbWVfdG9fMTIwNjIwMTZfbGVmdG92ZXJzX2dlb2NvZGUgPC0gcmVhZF9yZHMoIi4vZGF0YS9jcmltZV90b18xMjA2MjAxNl9sZWZ0b3ZlcnNfZ2VvY29kZS5yZHMiKQoKI2N1dCBvdXQgdGhlIGNvbHVtbiB3ZSB1c2VkIGZvciBnZW9jb2RpbmcKY3JpbWVfdG9fMTIwNjIwMTZfbGVmdG92ZXJzX2dlb2NvZGUgPC0gCiAgY3JpbWVfdG9fMTIwNjIwMTZfbGVmdG92ZXJzX2dlb2NvZGUgJT4lIHNlbGVjdCgtZXh0cmFjdGVkX2FkZHJlc3MpCiAgCiNjdXQgb3V0IG9mIHRoZSBnZW9jb2RlIGZhaWx1cmVzIGFuZCBwdXQgdGhlIGxvY2F0aW9uIGluZm9ybWF0aW9uIGludG8gdGhlIGNvbHVtbnMgbGF0IGFuZCBsb24KY3JpbWVfdG9fMTIwNjIwMTZfbGVmdG92ZXJzX2dlb2NvZGUgPC0gCiAgcmVtb3ZlX251bGxfbG9jYXRpb25zKGNyaW1lX3RvXzEyMDYyMDE2X2xlZnRvdmVyc19nZW9jb2RlKSAlPiUKICB1bm5lc3QobG9jYXRpb24pCgojY3JlYXRlIGEgc2ltcGxlIGZlYXR1cmVzIChzZikgb2JqZWN0LCB1c2luZyB0aGUgbGF0aXRpdHVkZXMgYW5kIGxvbmdpdHVkZXMKY3JpbWVfdG9fMTIwNjIwMTZfbGVmdG92ZXJzX3NmIDwtIHN0X2FzX3NmKGNyaW1lX3RvXzEyMDYyMDE2X2xlZnRvdmVyc19nZW9jb2RlLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgY29vcmRzID0gYygibG9uIiwgImxhdCIpLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBjcnMgPSA0MzI2KQoKI2FwcGVuZCB0aGlzIHNpbXBsZSBmZWF0dXJlcyBkYXRhZnJhbWUgdG8gdGhlIGRhdGFmcmFtZSBmb3Igd2hpY2ggd2UgYWxyZWFkeSBoYWQgbG9jYXRpb25zCmNyaW1lX3RvXzEyMDYyMDE2X3NmIDwtIHJiaW5kKGNyaW1lX3RvXzEyMDYyMDE2X3NwbGl0W1sxXV0sIGNyaW1lX3RvXzEyMDYyMDE2X2xlZnRvdmVyc19zZikKCnJtKGNyaW1lX3RvXzEyMDYyMDE2LCBjcmltZV90b18xMjA2MjAxNl9sZWZ0b3ZlcnNfZ2VvY29kZSwgY3JpbWVfdG9fMTIwNjIwMTZfbGVmdG92ZXJzX3NmLCBjcmltZV90b18xMjA2MjAxNl9sZWZ0b3ZlcnMsIGNyaW1lX3RvXzEyMDYyMDE2X3NwbGl0KQoKYGBgCgoKYGBge3J9CgojY29uc2lkZXIgdGhlIGV4YW1wbGVzIGluIHRoZSByZWNlbnQgY3JpbWUgZGF0YSBmb3Igd2hpY2ggdGhlIGNvbnZlcnNpb24gdG8gc2YgZGlkbid0IHdvcmssIHJlbW92ZSB0aGUgdHdvIGNvbHVtbnMgdGhhdCB3ZSBoYXZlIGFkZGVkLCBhbmQgY3JlYXRlIGFuZCBhZGRyZXNzIGNvbHVtbiBmb3IgZ2VvY29kaW5nCmNyaW1lXzEyMDYyMDE2X3RvXzAzMTkyMDE4X2xlZnRvdmVycyA8LSBjcmltZV8xMjA2MjAxNl90b18wMzE5MjAxOF9zcGxpdFtbMl1dICU+JQogIHNlbGVjdCgtZXh0cmFjdGVkX2xhdCwgLWV4dHJhY3RlZF9sb24pICU+JQogIG11dGF0ZShleHRyYWN0ZWRfYWRkcmVzcyA9IHN0cl9jKGBJbmNpZGVudCBBZGRyZXNzYCwgIiwgRGV0cm9pdCwgTWljaGlnYW4iKSkKCmBgYAoKYGBge3IsIGV2YWwgPSBGQUxTRX0KCmNyaW1lXzEyMDYyMDE2X3RvXzAzMTkyMDE4X2dlb2NvZGUgPC0gY3JpbWVfMTIwNjIwMTZfdG9fMDMxOTIwMThfbGVmdG92ZXJzICU+JSAKICBtdXRhdGUobG9jYXRpb24gPSBtYXAoZXh0cmFjdGVkX2FkZHJlc3MsIGdlb2NvZGVfcGF1c2UpKQoKd3JpdGVfcmRzKGNyaW1lXzEyMDYyMDE2X3RvXzAzMTkyMDE4X2dlb2NvZGUsICIuL2RhdGEvY3JpbWVfMTIwNjIwMTZfdG9fMDMxOTIwMThfZ2VvY29kZS5yZHMiKQoKYGBgCgpgYGB7cn0KCmNyaW1lXzEyMDYyMDE2X3RvXzAzMTkyMDE4X2dlb2NvZGUgPC0gcmVhZF9yZHMoIi4vZGF0YS9jcmltZV8xMjA2MjAxNl90b18wMzE5MjAxOF9nZW9jb2RlLnJkcyIpICU+JQogIHNlbGVjdCgtZXh0cmFjdGVkX2FkZHJlc3MpCgojcmVtb3ZlIHRoZSByb3dzIGZvciB3aGljaCB0aGUgdmFsdWUgb2YgbG9jYXRpb24gaXMgTlVMTCBhbmQgdGhlbiB1bm5lc3QgdGhlIHJlbWFpbmluZyBsb2NhdGlvbnMKY3JpbWVfMTIwNjIwMTZfdG9fMDMxOTIwMThfZ2VvY29kZSA8LSAKICByZW1vdmVfbnVsbF9sb2NhdGlvbnMoY3JpbWVfMTIwNjIwMTZfdG9fMDMxOTIwMThfZ2VvY29kZSkgJT4lCiAgdW5uZXN0KGxvY2F0aW9uKQoKI2NvbnZlcnQgdGhlIGRhdGFmcmFtZSB0byBhIHNpbXBsZSBmZWF0dXJlcyBzZXQKY3JpbWVfMTIwNjIwMTZfdG9fMDMxOTIwMThfc2YgPC0gc3RfYXNfc2YoY3JpbWVfMTIwNjIwMTZfdG9fMDMxOTIwMThfZ2VvY29kZSwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGNvb3JkcyA9IGMoImxvbiIsICJsYXQiKSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgY3JzID0gNDMyNikgIAoKI2NvbWJpbmUgdGhlIGdlb2NvZGVkIGRhdGEgd2l0aCB0aGUgc2YgZGF0YWZyYW1lIGNyZWF0ZWQgZWFybGllcgpjcmltZV8xMjA2MjAxNl90b18wMzE5MjAxOCA8LSByYmluZChjcmltZV8xMjA2MjAxNl90b18wMzE5MjAxOF9zcGxpdFtbMV1dLCBjcmltZV8xMjA2MjAxNl90b18wMzE5MjAxOF9zZikKCnJtKGNyaW1lXzEyMDYyMDE2X3RvXzAzMTkyMDE4X3NwbGl0LCBjcmltZV8xMjA2MjAxNl90b18wMzE5MjAxOF9nZW9jb2RlLCBjcmltZV8xMjA2MjAxNl90b18wMzE5MjAxOF9sZWZ0b3ZlcnMsIGNyaW1lXzEyMDYyMDE2X3RvXzAzMTkyMDE4X3NmKQoKYGBgCgpgYGB7cn0KCiNnZW9jb2RlIHRoZSBvbmUgaXRlbSBpbiB0aGUgSW1wcm92ZSBEZXRyb2l0IElzc3VlcyBkYXRhIGZvciB3aGljaCB0aGUgZ2l2ZW4gY29vcmRpbmF0ZXMgd2VyZSBvYnZpb3VzbHkgaW5jb3JyZWN0LCBhbmQgdGhlbiBjb252ZXJ0IHRvIGFuIHNmIG9iamVjdC4gSWYgZ2VvY29kaW5nIGZhaWxzLCBydW4gdGhpcyBiaXQgYWdhaW4KaW1wcm92ZV9kZXRyb2l0X2lzc3Vlc19sZWZ0b3Zlcl9zZiA8LSBpbXByb3ZlX2RldHJvaXRfaXNzdWVzX3NwbGl0W1syXV0gJT4lCiAgc2VsZWN0KC1leHRyYWN0ZWRfbGF0LCAtZXh0cmFjdGVkX2xvbikgJT4lCiAgbXV0YXRlKGxvY2F0aW9uID0gbWFwKEFkZHJlc3MsIGdlb2NvZGVfcGF1c2UpKSAlPiUKICB1bm5lc3QobG9jYXRpb24pICU+JSAKICBzdF9hc19zZihjb29yZHMgPSBjKCJsb24iLCAibGF0IiksIGNycyA9IDQzMjYpICAKCiNzcGxpY2Ugd2l0aCB0aGUgcHJldmlvdXNseSBnZW5lcmF0ZWQgc2YgZGF0YWZyYW1lCmltcHJvdmVfZGV0cm9pdF9pc3N1ZXMgPC0gcmJpbmQoaW1wcm92ZV9kZXRyb2l0X2lzc3Vlc19zcGxpdFtbMV1dLCBpbXByb3ZlX2RldHJvaXRfaXNzdWVzX2xlZnRvdmVyX3NmKQoKcm0oaW1wcm92ZV9kZXRyb2l0X2lzc3Vlc19zcGxpdCwgaW1wcm92ZV9kZXRyb2l0X2lzc3Vlc19sZWZ0b3Zlcl9zZikKCmBgYAoKYGBge3J9CgojY3JlYXRlIHRoZSBvdGhlciBzZXQgb2YgZGVtb2xpdGlvbiBpbmZvcm1hdGlvbgpjb21wbGV0ZWRfZGVtb2xpdGlvbnNfc2YgPC0gY29tcGxldGVkX2RlbW9saXRpb25zX3NwbGl0W1sxXV0KCiNub3RlIHRoYXQgbG9jYXRpb24gaW5mb3JtYXRpb24gaW4gdGhpcyBkYXRhc2V0IGlzIGNvbXBsZXRlCmNvbXBsZXRlZF9kZW1vbGl0aW9uc19zcGxpdFtbMl1dCgpgYGAKV2UgYmVnaW4gdGhlIGFzc2lnbm1lbnQgb2YgbGFiZWxzIHRvIHRoZSBidWlkaW5nczogYmxpZ2h0ZWQgb3Igbm90IGJsaWdodGVkLiBCdWlsZGluZ3Mgd2lsbCBiZSByZXByZXNlbnRlZCBieSBwYXJjZWxzIHRoYXQgaGF2ZSBvciBoYXZlIGhhZCBidWlsZGluZ3Mgb24gdGhlbSwgd2hldGhlciBieSBiZWluZyBzbyByZXByZXNlbnRlZCBhcyBpbiB0aGUgYHBhcmNlbHNfc2ZgIGRhdGEgZnJhbWUgYXMgaW5jbHVkaW5nIHN0cnVjdHVyZXMgb3IgaW4gdGhlIGRpc21hbnRsZSBwZXJtaXRzIGRhdGFmcmFtZSBhcyBoYXZpbmcgaGFkIGEgZGlzbWFudGUgcGVybWl0IGFzc29jaWF0ZWQgd2l0aCBpdCwgdGh1cyBzdWdnZXN0aW5nIHRoYXQgdGhlcmUgKndhcyogYSBidWlsZGluZyBvbiB0aGUgcGFyY2VsLiAKCldlIHdpbGwgdXNlIHBhcmNlbCBudW1iZXJzIHRvIHJlZmVyIHRvIHRoZSBwYXJjZWxzLiBIb3dldmVyLCBhcyB0aGUgZm9sbG93aW5nIGJpdCBvZiBjb2RlIHNob3dzLCB0aGUgcGFyY2VscyBkYXRhc2V0IGNvbnRhaW5zIGEgZmV3IHJvd3MgaW4gd2hpY2ggdGhlIHBhcmNlbCBodW1iZXJzIGFyZSB0aGUgc2FtZSAoYGR1cGxpY2F0ZV9wYXJjZWxfbnVtYmVyc19pbl9wYXJjZWxfZGF0YWAgY29udGFpbnMgNzggcm93cykuIAoKYGBge3IsIHdhcm5pbmcgPSBGQUxTRX0KCnBhcmNlbF9zZiA8LSBwYXJjZWxfc2YgJT4lCiAgbXV0YXRlKHJvd19udW0gPSByb3dfbnVtYmVyKCkpCgojQXMgcGVyIGFib3ZlLCBmb2xsb3dpbmcgcmV0dXJucyBhIDc4LXJvdyBkYXRhIGZyYW1lCmR1cGxpY2F0ZV9wYXJjZWxfbnVtYmVyc19pbl9wYXJjZWxfZGF0YSA8LSAKICBwYXJjZWxfc2YgJT4lCiAgZ3JvdXBfYnkocGFyY2VsbnVtKSAlPiUgCiAgbXV0YXRlKG4gPSBuKCkpICU+JQogIHVuZ3JvdXAoKSAlPiUgCiAgZmlsdGVyKG4gPiAxKSAlPiUKICBzZWxlY3QocGFyY2VsbnVtLCBhZGRyZXNzLCBsZWdhbGRlc2MsIHJvd19udW0pCmR1cGxpY2F0ZV9wYXJjZWxfbnVtYmVyc19pbl9wYXJjZWxfZGF0YQoKYGBgCgpQbG90dGluZyB0aGUgcGFyY2VsIGRhdGEgc2hvd3MgdGhhdCwgd2l0aGluIGdyb3VwcyBvZiBwYXJjZWxzIHRoYXQgaGF2ZSB0aGUgc2FtZSBwYXJjZWwgbnVtYmVyLCBzb21lIG9mIHRoZSBwYWNyY2VscyBhcmUgZ2VvbWV0cmljYWxseSBpZGVudGljYWwgd2hpbGUgb3RoZXJzIGFyZSBkaXNqb2ludC4gV2UgdGh1cyBhIGFwcGx5IGEgc3BhdGlhbCBqb2luLCB1c2luZyBgc2Y6OnN0X2pvaW5gLCB0byBmaW5kLCBhbW9uZyBlYWNoIG9mIHRoZXNlIGdyb3VwcyBvZiBwYXJjZWxzLCB0aGUgcGFyY2VscyB3aXRoIGRpZmZlcmVudCBwYXJjZWwgbnVtYmVycyBidXQgd2hpY2ggYXJlIGdlb21ldHJpY2FsbHkgdGhlIHNhbWUuCgpgYGB7cn0KCiNqb2luIHRoZSByZXN1bHQgd2l0aCBpdHNlbGYsIG9uIHRoZSBiYXNpcyBvZiBzYW1lbmVzcyBvZiBpZGVudGl0eSBvZiBzcGF0aWFsIGlkZW50aXR5IApzcGF0aWFsX3JlcGVhdHMgPC0gc3Rfam9pbihkdXBsaWNhdGVfcGFyY2VsX251bWJlcnNfaW5fcGFyY2VsX2RhdGEsCiAgICAgICAgICAgICAgICAgICAgICAgICAgIGR1cGxpY2F0ZV9wYXJjZWxfbnVtYmVyc19pbl9wYXJjZWxfZGF0YSwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgIHN0X2VxdWFscywgbGVmdCA9IEZBTFNFKSAlPiUgZmlsdGVyKHJvd19udW0ueCAhPSByb3dfbnVtLnkpCgojc2VsZWN0IG9uZSBlbGVtZW50IGZyb20gZWFjaCBncm91cCB3aXRoIHRoZSBzZiBvYmplY3RzIGZvciB3aGljaCB0aGUgcG9seWdvbiBjb3ZlcnMgdGhlIHNhbWUgYXJlYQpzZWxlY3Rpb25fdmVjdG9yIDwtIDE6bnJvdyhzcGF0aWFsX3JlcGVhdHMpCmZvciAoaW5kZXggaW4gMTpucm93KHNwYXRpYWxfcmVwZWF0cykpIHsKICBzZWxlY3Rpb25fdmVjdG9yW2luZGV4XSA8LSAhKHNwYXRpYWxfcmVwZWF0cyRyb3dfbnVtLnhbaW5kZXhdICVpbiUgc3BhdGlhbF9yZXBlYXRzJHJvd19udW0ueVsxOmluZGV4XSkgCn0Kc2VsZWN0aW9uX3ZlY3RvciA8LSBhcy5sb2dpY2FsKHNlbGVjdGlvbl92ZWN0b3IpCgojdGhlIHVuaXF1ZSBwYXJjZWxzLCBhcyBkZXNjcmliZWQgaW4gdW5pcXVlX3BhcmNlbHMKdW5pcXVlX3BhcmNlbHMgPC0gc3BhdGlhbF9yZXBlYXRzW3NlbGVjdGlvbl92ZWN0b3IsXQoKI3RoZSB1bmlxdWUgcGFyY2VscywgYXMgZGVzY3JpYmVkIGluIHBhcmNlbF9zZiwgd2l0aCBhIHJvdyBudW1iZXIgYWRkZWQgdG8gdGhlIGVuZCBvZiB0aGUgcGFyY2VsIG51bWJlciAoY2hhcmFjdGVyIHZlY3RvcikKdW5pcXVlX3BhcmNlbHMgPC0gcGFyY2VsX3NmICU+JSBmaWx0ZXIocm93X251bSAlaW4lIHVuaXF1ZV9wYXJjZWxzJHJvd19udW0ueCkgJT4lCiAgbXV0YXRlKHBhcmNlbG51bSA9IHN0cl9jKHBhcmNlbG51bSwgIl8iLCByb3dfbnVtYmVyKCkpKQoKI2N1dCBvdXQgdGhlIHNldCBvZiBzZiBvYmplY3RzIHRoYXQgaGF2ZSBzcGF0aWFsIHJlcGVhdHMgIApwYXJjZWxfc2YgPC0gcGFyY2VsX3NmICU+JSBmaWx0ZXIoIShyb3dfbnVtICVpbiUgc3BhdGlhbF9yZXBlYXRzJHJvd19udW0ueCkpCgojYmluZCB0aGUgdGhlIHNldCBvZiB1bmlxdWUgc3BhdGlhbCBvYmplY3RzIHRvIHBhcmNlbF9zZgpwYXJjZWxfc2YgPC0gcGFyY2VsX3NmICU+JSByYmluZCh1bmlxdWVfcGFyY2VscykKCiNjaGVjayBmb3Igb3ZlcmxhcCBvZiB0aGUgcGFyY2VscyBpbiBwYXJjZWxfc2YKc3BhdGlhbF9yZXBlYXRzIDwtIHN0X2pvaW4ocGFyY2VsX3NmLCBwYXJjZWxfc2YsIAogICAgICAgIHN0X292ZXJsYXBzLCBsZWZ0ID0gRkFMU0UpICU+JSBmaWx0ZXIocm93X251bS54ICE9IHJvd19udW0ueSkKCiNjcmVhdGUgdmVjdG9ycyBvciByb3cgbnVtYmVycyBpbiBwYXJjZWxfc2Ygd2hvc2UgY29ycmVzcG9uZGluZyBlbGVtZW50cyBzcGVjaWZ5IG9iamVjdHMgaW4gcGFyY2VsX3NmIHdpdGggb3ZlcmxhcHBpbmcgZ2VvbWV0cmllcy4KeF9yb3dzIDwtIHNwYXRpYWxfcmVwZWF0cyRyb3dfbnVtLngKeV9yb3dzIDwtIHNwYXRpYWxfcmVwZWF0cyRyb3dfbnVtLnkKCiNleGFtaW5lIGEgcmFuZG9tIHNlbGVjdGlvbiBvZiB0aGVzZSBjYXNlcwpzZXQuc2VlZCg1NSk7IHNhbXBsZSA8LSBzYW1wbGUoMTpucm93KHNwYXRpYWxfcmVwZWF0cyksIDIwKQpwYXJjZWxfc2VsZWN0aW9uIDwtIHNwYXRpYWxfcmVwZWF0c1tzYW1wbGUsXSAlPiUgc2VsZWN0KHJvd19udW0ueCwgcm93X251bS55KQoKcGxvdHMgPC0gbWFwKDE6MjAsIGZ1bmN0aW9uKGluZGV4KXsKICByb3dfMSA8LSAocGFyY2VsX3NmICU+JSBzZWxlY3QocGFyY2VsbnVtKSlbcGFyY2VsX3NlbGVjdGlvbiRyb3dfbnVtLnhbaW5kZXhdLF0KICByb3dfMiA8LSAocGFyY2VsX3NmICU+JSBzZWxlY3QocGFyY2VsbnVtKSlbcGFyY2VsX3NlbGVjdGlvbiRyb3dfbnVtLnlbaW5kZXhdLF0KICBwbG90KHJiaW5kKHJvd18xLCByb3dfMikpCn0pCgojcmVtb3ZlIHRoZSByb3dfbnVtIHZhcmlhYmxlIGZyb20gcHJhY2VsX3NmCnBhcmNlbF9zZiA8LSBwYXJjZWxfc2YgJT4lIHNlbGVjdCgtcm93X251bSkKCnJtKGR1cGxpY2F0ZV9wYXJjZWxfbnVtYmVyc19pbl9wYXJjZWxfZGF0YSwgc2VsZWN0aW9uX3ZlY3RvciwgdW5pcXVlX3BhcmNlbHMpCgpgYGAKClRoZSBkaXNtYW50bGUgcGVybWl0cyBkYXRhIGFsc28gY29udGFpbnMgc29tZSBkdXBsaWNhdGUgcGFyY2VsIG51bWJlcnMgb3ZlciByb3dzLCBpbiBzb21lIGNhc2VzIG92ZXIgcm93cyB0aGF0IGNvbnRhaW4gYWRkcmVzcyBpbmZvcm1hdGlvbiBzdWdnZXN0aW5nIHRoYXQgdGhlIGxvY2F0aW9uIGlzIGRpZmZlcmVudC4gKEl0IGFsc28gaW5kaWNhdGVzIHRoYXQgYSBmZXcgaW5kaXZpZHVhbCBsb2NhdGF0aW9ucywgaWRlbnRpZmllZCB3aXRoIGFkZHJlc3NlcywgaGFkIG1vcmUgdGhhbiBvbmUgYXNzb2NpYXRlZCBkaXNtYW50bGUgcGVybWl0LiBUaGlzIG5lZWQgbm90IGJlIHByb2JsZW1hdGljLS0tYSBwZXJtaXQgY291bGQgZXhwaXJlIGJlZm9yZSB0aGUgd29yayBpcyBjYXJyaWVkIG91dCwgb3IgdGhlcmUgY291bGQgYmUgbW9yZSB0aGFuIG9uZSBzdHJ1Y3R1cmUgb24gYSBwYXJjZWwuKSAgVGhlc2UgcmVwZXRpdGlvbnMgYXJlIGluIGBkdXBsaWNhdGVfcGFyY2VsX251bWJlcnNfb3Zlcl9kaXN0aW5jdF9hZGRyZXNzZXNgLCB3aGljaCBjb250YWlucyAyNjAgcm93cy4gSSBzaG91bGQgYWxzbyBub3RlIHRoYXQsIGluICBtb3N0IG9mIHRoZXNlIGNhc2VzIHdpdGggZHVwbGljYXRlIHBhcmNlbCBudW1iZXJzIChhbmQgYWRkcmVzc2VzIGluZGljYXRpbmcgZGlmZmVyZW50IGxvY2F0aW9ucyksIHRoZSByZWNvcmRlZCBsYXRpdHVkZSBhbmQgbG9uZ2l0dWRlIGFyZSBpZGVudGljYWwuIFdlIGNhbiB0aHVzIGluZmVyIHRoYXQgc29tZSBvZiB0aGlzIGxvY2F0aW9uIGluZm9ybWF0aW9uIGlzIGluY29ycmVjdC4KYGBge3IsIHdhcm5pbmcgPSBGQUxTRX0KCiNyZXBlYXRlZCBwYXJjZWwgbnVtYmVycyBpbiB0aGUgZGlzbWFudGxlIHBlcm1pdHMgZGF0YQpkdXBfcGFyX251bV9pbl9kaXNtYW50bGVfZGF0YSA8LQogIGRpc21hbnRsZV9wZXJtaXRzX3NmICU+JQogIGdyb3VwX2J5KGBQYXJjZWwgTnVtYmVyYCkgJT4lIAogIG11dGF0ZShuID0gbigpKSAlPiUKICB1bmdyb3VwKCkgJT4lIAogIGZpbHRlcihuID4gMSkgJT4lCiAgc2VsZWN0KGBQYXJjZWwgTnVtYmVyYCwgYFNpdGUgQWRkcmVzc2ApICU+JQogIGFycmFuZ2UoYFBhcmNlbCBOdW1iZXJgKQpybShkdXBfcGFyX251bV9pbl9kaXNtYW50bGVfZGF0YSkKCiNwYXJjZWwgbnVtYmVycyBpbiB0aGUgZGlzbWFudGxlIHBlcm1pdHMgZGF0YSB0aGF0IGFyZSBkaXN0cmlidXRlZCBvdmVyIGRpc2luY3QgYWRkcmVzcyBzdHJpbmdzCmR1cF9wYXJfbnVtX292ZXJfZGlzdGluY3RfYWRkcmVzc2VzIDwtIAogIGRpc21hbnRsZV9wZXJtaXRzX3NmICU+JQogIGdyb3VwX2J5KGBQYXJjZWwgTnVtYmVyYCkgJT4lCiAgbXV0YXRlKHBhcmNlbF9udW1iZXJfb2NjdXJhbmNlcyA9IG4oKSkgJT4lCiAgdW5ncm91cCgpICU+JQogIGZpbHRlcihwYXJjZWxfbnVtYmVyX29jY3VyYW5jZXMgPiAxKSAlPiUKICBncm91cF9ieShgUGFyY2VsIE51bWJlcmAsIGBTaXRlIEFkZHJlc3NgKSAlPiUKICBtdXRhdGUobSA9IG4oKSkgJT4lCiAgZmlsdGVyKG0gPCBwYXJjZWxfbnVtYmVyX29jY3VyYW5jZXMpICU+JQogIGFycmFuZ2UoYFBhcmNlbCBOdW1iZXJgKQoKI3ZpZXcgc2VsZWN0ZWQgcG9ydGlvbnMgb2YgZHVwX3Bhcl9udW1fb3Zlcl9kaXN0aW5jdF9hZGRyZXNzZXMKYXMuZGF0YS5mcmFtZShkdXBfcGFyX251bV9vdmVyX2Rpc3RpbmN0X2FkZHJlc3NlcykgJT4lCiAgc2VsZWN0KGBQYXJjZWwgTnVtYmVyYCwgYFNpdGUgQWRkcmVzc2AsIHBhcmNlbF9udW1iZXJfb2NjdXJhbmNlcywgbSkKCmBgYAoKYGBge3IsIGV2YWwgPSBGQUxTRX0KCiNyZW1vdmUgZXh0cmFuZW91cyB2YXJpYWJsZXMgYW5kIHRoZW4gZ2VvY29kZSB0aGUgZGlzbWFudGxlIHNpdGUgYWRkcmVzc2VzCmdlb2NvZGVkX2R1cGxpY2F0ZXMgPC0gZHVwX3Bhcl9udW1fb3Zlcl9kaXN0aW5jdF9hZGRyZXNzZXMgJT4lCiAgYXMuZGF0YS5mcmFtZSAlPiUKICBzZWxlY3QoLXBhcmNlbF9udW1iZXJfb2NjdXJhbmNlcywgLW0sIC1nZW9tZXRyeSkgJT4lCiAgbXV0YXRlKGxvY2F0aW9uID0gbWFwKHN0cl9jKGBTaXRlIEFkZHJlc3NgLCAiLCBEZXRyb2l0LCBNaWNoaWdhbiIpLCBnZW9jb2RlX3BhdXNlKSkKICAKI2F2b2lkIGhhdmluZyB0byBkbyB0aGUgZ2VvY29kaW5nIGFnYWluIHdoZW4gcmUtcnVubmluZyB0aGUgY29kZQp3cml0ZV9yZHMoZ2VvY29kZWRfZHVwbGljYXRlcywgIi4vZGF0YS9nZW9jb2RlZF9kdXBsaWNhdGVzLnJkcyIpICAKCmBgYAoKSGF2aW5nIGlkZW50aWZpZWQgc29tZSBwb3RlbnRpYWxseSBwcm9ibGVtYXRpYyBkYXRhIHBvaW50cywgd2UgbmVlZCB0byBpbnZlc3RpZ2F0ZSBhIGZldyBvZiB0aGVtLiBBcyBpbiB0aGUgZm9sbG93aW5nIGV4YW1wbGUuCgpgYGB7ciwgd2FybmluZyA9IEZBTFNFfQoKdGVtcDEgPC0gcmVhZF9yZHMoIi4vZGF0YS9nZW9jb2RlZF9kdXBsaWNhdGVzLnJkcyIpCgojSXNvbGF0ZSBzb21lIHZhcmlhYmxlcyBmb3Igdmlld2luZwp0ZW1wMiA8LSB0ZW1wMSAlPiUgc2VsZWN0KGBQYXJjZWwgTnVtYmVyYCwgbG9jYXRpb24sIGBQZXJtaXQgTG9jYXRpb25gKQoKI2VsZXZlbiBzZWVtaW5seSBzZXBhcmF0ZSBsb2NhdGlvbnMgd2l0aCB0aGUgc2FtZSBsaXN0ZWQgcGFyY2VsIG51bWJlcgp0ZW1wMyA8LSB0ZW1wMSAlPiUgZmlsdGVyKGBQYXJjZWwgTnVtYmVyYCA9PSAiMTMwMDY4MDkuIikKCiNwdXQgaW50byBhbiBzZiBmb3JtCnRlbXA0IDwtIHN0X2FzX3NmKHVubmVzdCh0ZW1wMyksIGNvb3JkcyA9IGMoImxvbiIsICJsYXQiKSwgY3JzID0gc3RfY3JzKHBhcmNlbF9zZikpCgojZnVuY3Rpb24gZm9yIHB1dHRpbmcgYSBzZXQgb2Ygb2Ygc2YgcG9pbnQgb24gYSBzYXRlbGl0ZSBtYXAKc2ZfcG9pbnRzX21hcCA8LSBmdW5jdGlvbihzZl9kZikgewogIGRmIDwtIHNmX2RmICU+JSBtdXRhdGUobG9uZ2l0dWRlID0gc3RfY29vcmRpbmF0ZXMoc2ZfZGYpWywxXSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBsYXRpdHVkZSA9IHN0X2Nvb3JkaW5hdGVzKHNmX2RmKVssMl0pICU+JQogIGFzLmRhdGEuZnJhbWUgJT4lIHNlbGVjdChsb25naXR1ZGUsIGxhdGl0dWRlKQogIAogICNsZWZ0L2JvdHRvbS9yaWdodC90b3AgZm9yIGJvdW5kaW5nIGJveAogIGJvdW5kaW5nX2JveCA8LSBjKG1pbihkZiRsb25naXR1ZGUpIC0gMC4wMDIsIG1pbihkZiRsYXRpdHVkZSkgLSAwLjAwMiwgCiAgICAgICAgICAgICAgICAgICAgbWF4KGRmJGxvbmdpdHVkZSkgKyAwLjAwMiwgbWF4KGRmJGxhdGl0dWRlKSArIDAuMDAyKQogIGRldHJvaXRfZ2cgPC0gZ2V0X21hcChsb2NhdGlvbiA9IGJvdW5kaW5nX2JveCwgCiAgICAgICAgICAgICAgICAgICAgICAgIG1hcHR5cGUgPSAic2F0ZWxsaXRlIikKICBnZ21hcChkZXRyb2l0X2dnKSArIGdlb21fcG9pbnQoZGF0YSA9IGRmLCBhZXMoeCA9IGxvbmdpdHVkZSwgeSA9IGxhdGl0dWRlKSkgCn0KCiNtYXAgb2YgcG9pbnRzIHdpdGggUGFyY2VsIE51bWJlciBsaXN0ZWQgYXMgIjEzMDA2ODA5LiIKc2ZfcG9pbnRzX21hcCh0ZW1wNCkKCiNJdCBpcyBhbHNvIG5vdGFibGUgdGhhdCAiMTMwMDY4MDkuIiBpcyBub3QgcmVmbGVjdGVkIGFzIGEgcGFyY2VsIG51bWJlciBpbiBwYXJjZWxfc2YgKG5vdGluZyB0aGF0IHBhcmNlbF9zZiRwYXJjZWxudW0gaXMgYW4gaW50ZWdlciB2ZWN0b3IpCnBhcmNlbF9zZiAlPiUgZmlsdGVyKHBhcmNlbG51bSA9PSBhcy5pbnRlZ2VyKCIxMzAwNjgwOS4iKSkKCiN0cnkgYSBzcGFjaWFsIGpvaW4gdG8gc2VlIHdoZXJlIHRoZXNlIHBvaW50cyBpbiB0aGUgZGlzbWFudGxlZCBkYXRhc2V0IGhvb2sgdXAgd2l0aCB0aGUgcGFyY2VscyBkYXRhc2V0CnRlbXA1IDwtIHN0X2pvaW4ocGFyY2VsX3NmLCB0ZW1wNCwgbGVmdCA9IEZBTFNFLCBzdF9jb250YWlucykKdGVtcDUkYFBhcmNlbCBOdW1iZXJgCiN0aHJlZSByb3dzLCBhbGwgd2l0aCBgUGFyY2VsIE51bWJlcmAgPSAiMTMwMDY4MDkuIgoKI3Bsb3Qgd2l0aCBjcnMgPSAzODU3IHRvIG1hdGNoIGdvb2dsZSBtYXBzCnNmX3BvaW50c19tYXAodGVtcDQpICsgIAogIGdlb21fc2YoZGF0YSA9IHRlbXA1WzEsXSAlPiUgc2VsZWN0KGdlb21ldHJ5KSwgY3JzID0gMzg1NywgaW5oZXJpdC5hZXMgPSBGQUxTRSkKCnJtKHRlbXAxLCB0ZW1wMiwgdGVtcDMsIHRlbXA0LCB0ZW1wNSkKYGBgCgpXZSB0aHVzIHNlZSB0aGF0IGF0IGxlYXN0IHNvbWUgb2YgdGhlIHBhcmNlbHMgY29udGFpbiBtdWx0aXBsZSBzdHJ1Y3R1cmVzLiBJdCBpcyBhbHNvIG5vdGFibGUgdGhhdCBzb21lIG9mIHRoZSBwb3NpdGlvbnMgYXNzb2NpYXRlZCB3aXRoIHRoZSBkaXNtYW50bGUgcGVybWl0cywgcGFydGljdWxhcmx5IHRob3NlIGluIHRoZSBzdHJlZXRzLCBhcmUgZmFyIGVub3VnaCBmcm9tIHRoZSB0aGUgcGFyY2VsIHRvIHBvdGVudGlhbGx5IGJlIGNsb3NlciB0byBvdGhlciBwYXJjZWxzLiBJbiBwYXJ0aWN1bGFyLCBpbnZlc3RpZ2F0aW9uIG9mIHRoZSBzdXJyb3VuZGluZyBhcmVhIHdpdGggZ29vZ2xlIGVhcnRoIHN1Z2dlc3RzIHRoYXQgdGhlIHBvaW50IGluIHRlbXA0IHRoYXQgaXMgdGhlIGZ1cnRoZXN0IHNvdXRoIGlzIHF1aXRlIGNsb3NlIHRvIGEgZGlzbW50bGVkIGJ1aWxkaW5nIHRoYXQgaXMgd2VsbC1vdXRzaWRlIG9mIHRoZSBwYXJjZWwuCgpXZSByZXN1bWUgdGhlIGRldmVsb3BtZW50IG91ciBvdXIgbW9kZWwuCgpgYGB7cn0KCiNyZWFkIGluIHRoZSBzYXZlZCBzZXQgb2YgZXhhbXBsZXMgdGhhdCBpbnZvbHZlIHJlcGVhdHMgb2YgcGFyY2VsIG51bWJlcnMgb3ZlciBkaWZmZXJlbnQgYWRkcmVzc2VzLgpkaXNtYW50bGVfZHVwbGljYXRlc19nZW9jb2RlZCA8LSByZWFkX3JkcygiLi9kYXRhL2dlb2NvZGVkX2R1cGxpY2F0ZXMucmRzIikKCiN1bnBhY2sgdGhlIGNvbHVtbiBvZiBkYXRhIGZyYW1lcyAodGhlIG91dHB1dHMgb2YgZ2VvY29kZSgpKQpkaXNtYW50bGVfZHVwbGljYXRlc19nZW9jb2RlZCA8LSByZW1vdmVfbnVsbF9sb2NhdGlvbnMoZGlzbWFudGxlX2R1cGxpY2F0ZXNfZ2VvY29kZWQpICU+JQogICB1bm5lc3QobG9jYXRpb24pCgojbm90ZSB0aGUgMTYgcm93cyBvZiBkaXNtYW50bGVfZHVwbGljYXRlc19nZW9jb2RlZCBmb3Igd2hpY2ggYW5vdGhlciByb3cgbGlzdHMgdGhlIHNhbWUgbG9uZ2l0dWRlIGFuZCBsYXRpdHVkZQpkaXNtYW50bGVfZHVwbGljYXRlc19nZW9jb2RlZCAlPiUgCiAgZ3JvdXBfYnkobG9uLCBsYXQpICU+JSBtdXRhdGUobiA9IG4oKSkgJT4lIGZpbHRlcihuID4gMSkKICAKI2tlZXAgb25seSB0aGUgZmlyc3Qgb2YgYW55IGdyb3VwIG9mIHJvd3Mgd2l0aCB0aGUgc2FtZSBsb25naXR1ZGUgYW5kIGxhdGl0dWRlCmRpc21hbnRsZV9kdXBsaWNhdGVzX2dlb2NvZGVkIDwtCiAgZGlzbWFudGxlX2R1cGxpY2F0ZXNfZ2VvY29kZWQgJT4lIGRpc3RpbmN0KGxvbiwgbGF0LCAua2VlcF9hbGwgPSBUUlVFKQpgYGAKClVzaW5nIEdvb2dsZSBTdHJlZXQgVmlldyB0byBpbnZlc3RpZ2F0ZSBzb21lIG1vcmUgZGlzbWFudGxlIHBlcm1pdCBlbnRyaWVzIGZvciB3aGljaCB0aGVyZSBhcmUgb3RoZXIgZW50cmllcyB3aXRoIHRoZSBzYW1lIHBhcmNlbCBudW1iZXIgYnV0IGRpZmZlcmVudCBhZGRyZXNzZXMsIHdlIGZpbmQgbW9zdGx5IGRpZmZlcmVudCBsb2NhdGlvbnMgYXQgdGhlIHNhbWUgYnVpZGxpbmcgKGUuZy4gdGhlIHR3byBzaWRlcyBvZiBhIGR1cGxleCksIG9yIHBlcmhhcHMgdHdvIGF0dGFjaGVkIGJ1aWxkaW5ncy4gCgpDb250aW51aW5nIG91ciBjb25zdHJ1Y3Rpb246CgpgYGB7ciwgZXZhbCA9IEZBTFNFfQogIAojbWFudWFsbHkgY3V0IG91dCB0aGUgb25lIHJlbWFpbmluZyBhcHBhcmVudCBkdXBsaWNhdGUgbG9jYXRpb24KZGlzbWFudGxlX2R1cGxpY2F0ZXNfZ2VvY29kZWQgPC0gZGlzbWFudGxlX2R1cGxpY2F0ZXNfZ2VvY29kZWQgJT4lIAogIGZpbHRlcighKGBTaXRlIEFkZHJlc3NgID09ICIzMjAwIEUgTEFGQVlFVFRFLU1BUlRJTiBMVVRIRVIgS0lORyBISUdIIikpCgpkaXNtYW50bGVfZHVwbGljYXRlc19nZW9jb2RlZCA8LSBkaXNtYW50bGVfZHVwbGljYXRlc19nZW9jb2RlZCAlPiUKICBzdF9hc19zZihjb29yZHMgPSBjKCJsb24iLCAibGF0IiksIGNycyA9IDQzMjYpCgojSXQgaXMgb2RkIHRoYXQgdGhpcyBzdGVwIGlzIG5lY2Vzc2FyeSwgZ2l2ZW4gdGhhdCBDUlNzIG9mIHRoZSB0d28gZGF0YSBmcmFtZXMgYXBwZWFyIHRvIGJlIHRoZSBzYW1lCnN0X2NycyhkaXNtYW50bGVfZHVwbGljYXRlc19nZW9jb2RlZCkgPC0gc3RfY3JzKHBhcmNlbF9zZikKCiN3aGVyZSBwb3NzaWJsZSwgZmluZCB0aGUgcGFyY2VscyB0aGF0IGNvbnRhaW4gdGhlIHBvc2l0aW9ucyBjb3JyZXNwb25kaW5nIHRvIHRoZSBjb29yZGluYXRlcwpzcGFjaWFsX2pvaW5fd2l0aGluIDwtIAogIHN0X2pvaW4oZGlzbWFudGxlX2R1cGxpY2F0ZXNfZ2VvY29kZWQsIAogICAgICAgICAgcGFyY2VsX3NmICU+JSBzZWxlY3QocGFyY2VsbnVtKSwgCiAgICAgICAgICBzdF93aXRoaW4pCgojY2hhbmdlIHRoZSBwYXJjZWwgbnVtYmVycyAoZm9yIHRoZSBkaXNtYW50bGUgcGVybWl0cyBkYXRhKSB0byB0aG9zZSBmb3IgdGhlIHBhcmNlbHMgdGhhdCBjb250YWluIHRoZSBwYXJjZWxzIHNwZWNpZmllZCBieSB0aGUgY29vcmRpbmF0ZXMuIHJlbW92ZSB0aGUgcGFyY2VsbnVtIGNvbHVtbiAoZnJvbSB0aGUgcGFyY2VsX3NmIGRhdGEgZnJhbWUpCmRpc21hbnRsZV9kdXBsaWNhdGVzX2dlb2NvZGVkIDwtIHNwYWNpYWxfam9pbl93aXRoaW4gJT4lCiAgbXV0YXRlKGBQYXJjZWwgTnVtYmVyYCA9IGlmZWxzZSghaXMubmEocGFyY2VsbnVtKSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGFzLmNoYXJhY3RlcihwYXJjZWxudW0pLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgYFBhcmNlbCBOdW1iZXJgKSkgJT4lCiAgc2VsZWN0KC1wYXJjZWxudW0pCgojcmVtb3ZlIGZyb20gZGlzbWFudGxlX3Blcm1pdHNfc2YgYWxsIG9mIHRoZSByb3dzIHdpdGggcmVwZWF0ZWQgcGFyY2VsIG51bWJlcnMgYW5kIHRoZW4KI3JlcGxhY2UgdGhlbiwgd2hlbiBwb3NzaWJsZSwgd2l0aCBnZW9jb2RlZCByb3dzLiBmaXJzdCBtYWtlIHRoZSBDUlNzIGNvbnNpc3RlbnQgKHdpdGggdGhlIG9ic2VydmF0aW9uLCBhZ2FpbiwgdGhhdCB0aGV5IHdvdWxkIGFwcGVhciB0byBhbHJlYWR5IGJlIHRoZSBzbykuCnN0X2NycyhkaXNtYW50bGVfZHVwbGljYXRlc19nZW9jb2RlZCkgPC0gc3RfY3JzKGRpc21hbnRsZV9wZXJtaXRzX3NmKQoKI1BFUkhBUFMgQ0hBTkdFIFRISVMuLi4KZGlzbWFudGxlX3Blcm1pdHNfc2YgPC0gZGlzbWFudGxlX3Blcm1pdHNfc2YgJT4lIAogIGZpbHRlcighKGBQYXJjZWwgTnVtYmVyYCAlaW4lIGR1cF9wYXJfbnVtX292ZXJfZGlzdGluY3RfYWRkcmVzc2VzJGBQYXJjZWwgTnVtYmVyYCkpICU+JQogIHJiaW5kKGRpc21hbnRsZV9kdXBsaWNhdGVzX2dlb2NvZGVkKQoKcm0oZGlzbWFudGxlX2R1cGxpY2F0ZXNfZ2VvY29kZWQsIGR1cF9wYXJfbnVtX292ZXJfZGlzdGluY3RfYWRkcmVzc2VzKQoKYGBgCgpXZSBub3cgYmVnaW4gdGhlIHByb2Nlc3Mgb2YgY3JlYXRpbmcgYSBzZXQgb2YgbGFiZWxzIG92ZXIgdGhlIGJ1aWxkaW5ncy4gVGhlIGZpcnN0IHN0ZXAgd2lsbCBiZSB0byBjcmVhdGUgYSBkYXRhZnJhbWUgaW4gd2hpY2ggZWFjaCByb3cgaXMgYSAiYnVpbGRpbmciLS0tdGhlIHNldCBwYXJjZWxzIChlbGVtZW50cyBvZiBgcGFyY2VsX3NmYCkgc3VjaCB0aGF0IGVhY2ggZWl0aGVyICgxKSBoYXMgYSBidWlsZGluZyBvbiBpdCwgYXMgaW5kaWNhdGVkIGluIGBwYXJjZWxfc2ZgLCBvciAoMikgYSBkaXNtYW50bGUgcGVybWl0IGZvciB0aGUgc2FtZSBsb2NhdGlvbiBhcyB0aGUgcGFyY2VsLiBJIHdpbGwgYmVnaW4gd2l0aCB0aGUgaWRlbnRpZmljYXRpb24gb2YgdGhlIGJ1aWxkaW5ncy4KCmBgYHtyLCBldmFsID0gRkFMU0V9CmJ1aWxkaW5ncyA8LSBwYXJjZWxfc2YgIyU+JSAKCnR5cGVvZihwYXJjZWxfc2YkcGFyY2VsbnVtKQp0eXBlb2YoZGlzbWFudGxlX3Blcm1pdHNfc2YkYFBhcmNlbCBOdW1iZXJgKQpgYGAKCgpgYGB7ciwgZXZhbCA9IEZBTFNFfQoKcGFyY2VsX2pvaW5fc2ZfcGFyY2VsX2ZpcnN0IDwtIAogIGlubmVyX2pvaW4ocGFyY2VsX3NmICU+JSBzZWxlY3QocGFyY2VsbnVtKSAlPiUgbXV0YXRlKHBhcmNlbG51bSA9IGFzLmNoYXJhY3RlcigocGFyY2VsbnVtKSkpLAogICAgICAgICAgICBkaXNtYW50bGVfcGVybWl0c19zZiAlPiUgYXMuZGF0YS5mcmFtZSgpICU+JSBzZWxlY3QoYFBhcmNlbCBOdW1iZXJgKSwKICAgICAgICAgICAgYygicGFyY2VsbnVtIiA9ICJQYXJjZWwgTnVtYmVyIikpICU+JQogIGFycmFuZ2UocGFyY2VsbnVtKQoKcGFyY2VsX2pvaW5fc2ZfZGlzbWFudGxlX2ZpcnN0IDwtCiAgZnVsbF9qb2luKGRpc21hbnRsZV9wZXJtaXRzX3NmICU+JSBzZWxlY3QoYFBhcmNlbCBOdW1iZXJgKSwgCiAgICAgICAgICAgIHBhcmNlbF9zZiAlPiUgYXMuZGF0YS5mcmFtZSgpICU+JSBzZWxlY3QocGFyY2VsbnVtKSwKICAgICAgICAgICAgYygiUGFyY2VsIE51bWJlciIgPSAicGFyY2VsbnVtIikpICU+JQogIGFycmFuZ2UoYFBhcmNlbCBOdW1iZXJgKQoKc3RfY3JzKHBhcmNlbF9zZikKc3RfY3JzKGRpc21hbnRsZV9wZXJtaXRzX3NmKQoKI0JvdGggb2YgdGhlc2UgeWllbGQgYW4gRVBTRyBvZiA0MzI2LiBOZXRoZXJlbGVzcywgdGhlIGZvbGxvdyBzdGVwIHNlZW1zIG5lY2Vzc2FyeS4KCnN0X2NycyhkaXNtYW50bGVfcGVybWl0c19zZikgPC0gc3RfY3JzKHBhcmNlbF9zZikKCnRlbXAgPC0gc3Rfam9pbihkaXNtYW50bGVfcGVybWl0c19zZiwgcGFyY2VsX3NmLCBqb2luID0gc3Rfd2l0aGluKQoKdGVtcDIgPC0gaW5uZXJfam9pbih0ZW1wLCBhcy5kYXRhLmZyYW1lKHBhcmNlbF9qb2luX3NmX3BhcmNlbF9maXJzdCksIGJ5ID0gInBhcmNlbG51bSIpCiAgCnRlbXAyIDwtIHRlbXAgJT4lIGZpbHRlcihgUGFyY2VsIE51bWJlcmAgIT0gcGFyY2VsbnVtKQoKdGVtcDIgPC0gdGVtcDIgJT4lIHNlbGVjdChgUGFyY2VsIE51bWJlcmAsIHBhcmNlbG51bSwgYFNpdGUgQWRkcmVzc2AsIGFkZHJlc3MpCgoKI25vdGUgdGhhdCBvYmplY3RpZCBpbiBwYXJjZWxfc2YgaGFzIG5vIE5BIHZhbHVlcywgYW5kIHRoYXQgb2JqZWN0aWQgaXMgYSBrZXkgaW4gcGFyY2VsX3NmCnBhcmNlbF9zZiAlPiUgYXMuZGF0YS5mcmFtZSgpICU+JSBmaWx0ZXIoaXMubmEob2JqZWN0aWQpKQpwYXJjZWxfc2YgJT4lIGNvdW50KG9iamVjdGlkKSAlPiUgZmlsdGVyKG4gPiAxKQoKI25vdGUgdGhhdCBgUGVybWl0IE51bWJlcmAgaXMgYSBhbHNvIGEga2V5LCBmb3IgZGlzbWFudGxlX3Blcm1pdHNfc2YKZGlzbWFudGxlX3Blcm1pdHNfc2YgJT4lIGFzLmRhdGEuZnJhbWUoKSAlPiUgZmlsdGVyKGlzLm5hKGBQZXJtaXQgTnVtYmVyYCkpCmRpc21hbnRsZV9wZXJtaXRzX3NmICU+JSBjb3VudChgUGVybWl0IE51bWJlcmApICU+JSBmaWx0ZXIobiA+IDEpCgojd2l0aCB0aGUgYWJvdmUgaW4gbWluZCwgd2UgZXhhbWluZSB0aGUgTkEgdmFsdWVzIGZvciB0aGVzZSBjb2x1bW5zIGluIG91ciBmdWxsIGpvaW4sIGJlZ2lubmluZyB3aXRoIG9iamVjdGlkIGluIHBhcmNlbF9zZi4gVGhlIGlkZWEgaXMgdGhhdCBhbnkgTkEgdmFsdWUgZm9yIG9iamVjdGlkIGluIHBhcmNlbF9qb2luIHdvdWxkIGNvcnJlc3BvbmQgdG8gYSBwYXJjZWwgaW4gdGhlIGRpc21hbnRsZSBwZXJtaXRzIGRhdGFzZXQgdGhhdCBpcyBlaXRoZXIgbm90IHJlZmxlY3RlZCBpbiB0aGUgcGFyY2VscyBkYXRhc2V0IG9yIGZvciB3aGljaCB0aGVyZSBpcyBhIGZsYXcgaW4gdGhlIGRhdGEuCnBhcmNlbF9qb2luICU+JSBmaWx0ZXIoaXMubmEob2JqZWN0aWQpKQoKZGlzbWFudGxlX3Blcm1pdHNfc2YgJT4lIGZpbHRlcihgUGFyY2VsIE51bWJlcmAgPT0gIjIyMTA4NTEyLiIpCgojZGlzbWFudGxlZF9zdHJ1Y3R1cmVzIDwtIGluaXRpYWxfam9pbiAlPiUgZmlsdGVyKCFpcy5uYShgUGVybWl0IE51bWJlcmApKQoKcGFyY2VsX3NmICU+JSBmaWx0ZXIocGFyY2VsbnVtID09ICIyMjEwODUxMi4iKQoKcGFyY2VsX3RtcCA8LSBibGlnaHRfdmlvbGF0aW9uc19zZltjKDEsIDIpLF0KCnN0X2Rpc3RhbmNlKHBhcmNlbF90bXApCgptX2xpc3QgPC0gbWFwcGx5KHN0X2Rpc3RhbmNlLCBibGlnaHRfdmlvbGF0aW9uc19zZiRnZW9tZXRyeVsxXSwgZnJlZCRnZW9tZXRyeSkKCmRpc21hbnRsZV9wZXJtaXRzX3NmICU+JSBhcy5kYXRhLmZyYW1lKCkgJT4lIGZpbHRlcihpcy5uYShgUGFyY2VsIE51bWJlcmApKQoKdGVtcDQgPC0gc3RfZGlzdGFuY2UocGFyY2VsX3NmWzE5NTA5OSxdLCAKICAgICAgICAgICAgYmxpZ2h0X3Zpb2xhdGlvbnNfc2ZbMjIyMzM6MjIyNTUsXSkKCnN0X2NycyhibGlnaHRfdmlvbGF0aW9uc19zZikgPC0gc3RfY3JzKHBhcmNlbF9zZikKCnBsb3QoYmxpZ2h0X3Zpb2xhdGlvbnNfc2YsIG1heC5wbG90ID0gMSkKCmBgYAoKTWFwczoKCmBgYHtyLCBldmFsID0gRkFMU0V9CmxpYnJhcnkoZ2dtYXApCgpnZ3Bsb3QoYmxpZ2h0X3Zpb2xhdGlvbnNfc2YgJT4lIHNlbGVjdChnZW9tZXRyeSkpICsKICBnZW9tX3NmKCkgKyAKICBnZW9tX2RlbnNpdHkyZChkYXRhID0gYmxpZ2h0X3Zpb2xhdGlvbnNfc2YgJT4lIHNlbGVjdChnZW9tZXRyeSkpCgoKZGV0cm9pdF9nZyA8LSBnZXRfbWFwKCJEZXRyb2l0IiwgbWFwdHlwZSA9ICJ0b25lci1saXRlIiwKICAgICAgICAgICAgICAgICAgICAgIHpvb20gPSAxMSkKCnRlbXAgPC0gZ2V0X21hcCgpCgpnZ21hcChkZXRyb2l0X2dnKQpgYGAKCgoKCgoK